Skip to content

Commit f3e3f6c

Browse files
committed
Avoid getting more than AccessShareLock when deparsing a query.
In make_ruledef and get_query_def, we have long used AcquireRewriteLocks to ensure that the querytree we are about to deparse is up-to-date and the schemas of the underlying relations aren't changing. Howwever, that function thinks the query is about to be executed, so it acquires locks that are stronger than necessary for the purpose of deparsing. Thus for example, if pg_dump asks to deparse a rule that includes "INSERT INTO t", we'd acquire RowExclusiveLock on t. That results in interference with concurrent transactions that might for example ask for ShareLock on t. Since pg_dump is documented as being purely read-only, this is unexpected. (Worse, it used to actually be read-only; this behavior dates back only to 8.1, cf commit ba42002.) Fix this by adding a parameter to AcquireRewriteLocks to tell it whether we want the "real" execution locks or only AccessShareLock. Report, diagnosis, and patch by Dean Rasheed. Back-patch to all supported branches.
1 parent 7552d3d commit f3e3f6c

File tree

3 files changed

+56
-23
lines changed

3 files changed

+56
-23
lines changed

src/backend/rewrite/rewriteHandler.c

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,13 @@ typedef struct rewrite_event
3737
CmdType event; /* type of rule being fired */
3838
} rewrite_event;
3939

40-
static bool acquireLocksOnSubLinks(Node *node, void *context);
40+
typedef struct acquireLocksOnSubLinks_context
41+
{
42+
bool for_execute; /* AcquireRewriteLocks' forExecute param */
43+
} acquireLocksOnSubLinks_context;
44+
45+
static bool acquireLocksOnSubLinks(Node *node,
46+
acquireLocksOnSubLinks_context *context);
4147
static Query *rewriteRuleAction(Query *parsetree,
4248
Query *rule_action,
4349
Node *rule_qual,
@@ -69,9 +75,19 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
6975
* These locks will ensure that the relation schemas don't change under us
7076
* while we are rewriting and planning the query.
7177
*
72-
* forUpdatePushedDown indicates that a pushed-down FOR UPDATE/SHARE applies
73-
* to the current subquery, requiring all rels to be opened with RowShareLock.
74-
* This should always be false at the start of the recursion.
78+
* forExecute indicates that the query is about to be executed.
79+
* If so, we'll acquire RowExclusiveLock on the query's resultRelation,
80+
* RowShareLock on any relation accessed FOR UPDATE/SHARE, and
81+
* AccessShareLock on all other relations mentioned.
82+
*
83+
* If forExecute is false, AccessShareLock is acquired on all relations.
84+
* This case is suitable for ruleutils.c, for example, where we only need
85+
* schema stability and we don't intend to actually modify any relations.
86+
*
87+
* forUpdatePushedDown indicates that a pushed-down FOR UPDATE/SHARE
88+
* applies to the current subquery, requiring all rels to be opened with at
89+
* least RowShareLock. This should always be false at the top of the
90+
* recursion. This flag is ignored if forExecute is false.
7591
*
7692
* A secondary purpose of this routine is to fix up JOIN RTE references to
7793
* dropped columns (see details below). Because the RTEs are modified in
@@ -99,10 +115,15 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
99115
* construction of a nested join was O(N^2) in the nesting depth.)
100116
*/
101117
void
102-
AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
118+
AcquireRewriteLocks(Query *parsetree,
119+
bool forExecute,
120+
bool forUpdatePushedDown)
103121
{
104122
ListCell *l;
105123
int rt_index;
124+
acquireLocksOnSubLinks_context context;
125+
126+
context.for_execute = forExecute;
106127

107128
/*
108129
* First, process RTEs of the current query level.
@@ -128,14 +149,12 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
128149
* release it until end of transaction. This protects the
129150
* rewriter and planner against schema changes mid-query.
130151
*
131-
* If the relation is the query's result relation, then we
132-
* need RowExclusiveLock. Otherwise, check to see if the
133-
* relation is accessed FOR UPDATE/SHARE or not. We can't
134-
* just grab AccessShareLock because then the executor would
135-
* be trying to upgrade the lock, leading to possible
136-
* deadlocks.
152+
* Assuming forExecute is true, this logic must match what the
153+
* executor will do, else we risk lock-upgrade deadlocks.
137154
*/
138-
if (rt_index == parsetree->resultRelation)
155+
if (!forExecute)
156+
lockmode = AccessShareLock;
157+
else if (rt_index == parsetree->resultRelation)
139158
lockmode = RowExclusiveLock;
140159
else if (forUpdatePushedDown ||
141160
get_parse_rowmark(parsetree, rt_index) != NULL)
@@ -223,6 +242,7 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
223242
* recurse to process the represented subquery.
224243
*/
225244
AcquireRewriteLocks(rte->subquery,
245+
forExecute,
226246
(forUpdatePushedDown ||
227247
get_parse_rowmark(parsetree, rt_index) != NULL));
228248
break;
@@ -238,23 +258,23 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
238258
{
239259
CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
240260

241-
AcquireRewriteLocks((Query *) cte->ctequery, false);
261+
AcquireRewriteLocks((Query *) cte->ctequery, forExecute, false);
242262
}
243263

244264
/*
245265
* Recurse into sublink subqueries, too. But we already did the ones in
246266
* the rtable and cteList.
247267
*/
248268
if (parsetree->hasSubLinks)
249-
query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL,
269+
query_tree_walker(parsetree, acquireLocksOnSubLinks, &context,
250270
QTW_IGNORE_RC_SUBQUERIES);
251271
}
252272

253273
/*
254274
* Walker to find sublink subqueries for AcquireRewriteLocks
255275
*/
256276
static bool
257-
acquireLocksOnSubLinks(Node *node, void *context)
277+
acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context)
258278
{
259279
if (node == NULL)
260280
return false;
@@ -263,7 +283,9 @@ acquireLocksOnSubLinks(Node *node, void *context)
263283
SubLink *sub = (SubLink *) node;
264284

265285
/* Do what we came for */
266-
AcquireRewriteLocks((Query *) sub->subselect, false);
286+
AcquireRewriteLocks((Query *) sub->subselect,
287+
context->for_execute,
288+
false);
267289
/* Fall through to process lefthand args of SubLink */
268290
}
269291

@@ -305,6 +327,9 @@ rewriteRuleAction(Query *parsetree,
305327
int rt_length;
306328
Query *sub_action;
307329
Query **sub_action_ptr;
330+
acquireLocksOnSubLinks_context context;
331+
332+
context.for_execute = true;
308333

309334
/*
310335
* Make modifiable copies of rule action and qual (what we're passed are
@@ -316,8 +341,8 @@ rewriteRuleAction(Query *parsetree,
316341
/*
317342
* Acquire necessary locks and fix any deleted JOIN RTE entries.
318343
*/
319-
AcquireRewriteLocks(rule_action, false);
320-
(void) acquireLocksOnSubLinks(rule_qual, NULL);
344+
AcquireRewriteLocks(rule_action, true, false);
345+
(void) acquireLocksOnSubLinks(rule_qual, &context);
321346

322347
current_varno = rt_index;
323348
rt_length = list_length(parsetree->rtable);
@@ -1367,7 +1392,7 @@ ApplyRetrieveRule(Query *parsetree,
13671392
*/
13681393
rule_action = copyObject(linitial(rule->actions));
13691394

1370-
AcquireRewriteLocks(rule_action, forUpdatePushedDown);
1395+
AcquireRewriteLocks(rule_action, true, forUpdatePushedDown);
13711396

13721397
/*
13731398
* Recursively expand any view references inside the view.
@@ -1689,14 +1714,17 @@ CopyAndAddInvertedQual(Query *parsetree,
16891714
{
16901715
/* Don't scribble on the passed qual (it's in the relcache!) */
16911716
Node *new_qual = (Node *) copyObject(rule_qual);
1717+
acquireLocksOnSubLinks_context context;
1718+
1719+
context.for_execute = true;
16921720

16931721
/*
16941722
* In case there are subqueries in the qual, acquire necessary locks and
16951723
* fix any deleted JOIN RTE entries. (This is somewhat redundant with
16961724
* rewriteRuleAction, but not entirely ... consider restructuring so that
16971725
* we only need to process the qual this way once.)
16981726
*/
1699-
(void) acquireLocksOnSubLinks(new_qual, NULL);
1727+
(void) acquireLocksOnSubLinks(new_qual, &context);
17001728

17011729
/* Fix references to OLD */
17021730
ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);

src/backend/utils/adt/ruleutils.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,7 +2469,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
24692469
query = getInsertSelectQuery(query, NULL);
24702470

24712471
/* Must acquire locks right away; see notes in get_query_def() */
2472-
AcquireRewriteLocks(query, false);
2472+
AcquireRewriteLocks(query, false, false);
24732473

24742474
context.buf = buf;
24752475
context.namespaces = list_make1(&dpns);
@@ -2613,8 +2613,11 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
26132613
* relations, and fix up deleted columns in JOIN RTEs. This ensures
26142614
* consistent results. Note we assume it's OK to scribble on the passed
26152615
* querytree!
2616+
*
2617+
* We are only deparsing the query (we are not about to execute it), so we
2618+
* only need AccessShareLock on the relations it mentions.
26162619
*/
2617-
AcquireRewriteLocks(query, false);
2620+
AcquireRewriteLocks(query, false, false);
26182621

26192622
context.buf = buf;
26202623
context.namespaces = lcons(&dpns, list_copy(parentnamespace));

src/include/rewrite/rewriteHandler.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
#include "nodes/parsenodes.h"
1919

2020
extern List *QueryRewrite(Query *parsetree);
21-
extern void AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown);
21+
extern void AcquireRewriteLocks(Query *parsetree,
22+
bool forExecute,
23+
bool forUpdatePushedDown);
2224
extern Node *build_column_default(Relation rel, int attrno);
2325

2426
#endif /* REWRITEHANDLER_H */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy