Skip to content

Commit f043bdd

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 b6e1434 commit f043bdd

File tree

3 files changed

+46
-21
lines changed

3 files changed

+46
-21
lines changed

src/backend/rewrite/rewriteHandler.c

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

39-
static bool acquireLocksOnSubLinks(Node *node, void *context);
39+
typedef struct acquireLocksOnSubLinks_context
40+
{
41+
bool for_execute; /* AcquireRewriteLocks' forExecute param */
42+
} acquireLocksOnSubLinks_context;
43+
44+
static bool acquireLocksOnSubLinks(Node *node,
45+
acquireLocksOnSubLinks_context *context);
4046
static Query *rewriteRuleAction(Query *parsetree,
4147
Query *rule_action,
4248
Node *rule_qual,
@@ -65,6 +71,15 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
6571
* These locks will ensure that the relation schemas don't change under us
6672
* while we are rewriting and planning the query.
6773
*
74+
* forExecute indicates that the query is about to be executed.
75+
* If so, we'll acquire RowExclusiveLock on the query's resultRelation,
76+
* RowShareLock on any relation accessed FOR UPDATE/SHARE, and
77+
* AccessShareLock on all other relations mentioned.
78+
*
79+
* If forExecute is false, AccessShareLock is acquired on all relations.
80+
* This case is suitable for ruleutils.c, for example, where we only need
81+
* schema stability and we don't intend to actually modify any relations.
82+
*
6883
* A secondary purpose of this routine is to fix up JOIN RTE references to
6984
* dropped columns (see details below). Because the RTEs are modified in
7085
* place, it is generally appropriate for the caller of this routine to have
@@ -91,10 +106,13 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
91106
* construction of a nested join was O(N^2) in the nesting depth.)
92107
*/
93108
void
94-
AcquireRewriteLocks(Query *parsetree)
109+
AcquireRewriteLocks(Query *parsetree, bool forExecute)
95110
{
96111
ListCell *l;
97112
int rt_index;
113+
acquireLocksOnSubLinks_context context;
114+
115+
context.for_execute = forExecute;
98116

99117
/*
100118
* First, process RTEs of the current query level.
@@ -120,14 +138,12 @@ AcquireRewriteLocks(Query *parsetree)
120138
* release it until end of transaction. This protects the
121139
* rewriter and planner against schema changes mid-query.
122140
*
123-
* If the relation is the query's result relation, then we
124-
* need RowExclusiveLock. Otherwise, check to see if the
125-
* relation is accessed FOR UPDATE/SHARE or not. We can't
126-
* just grab AccessShareLock because then the executor would
127-
* be trying to upgrade the lock, leading to possible
128-
* deadlocks.
141+
* Assuming forExecute is true, this logic must match what the
142+
* executor will do, else we risk lock-upgrade deadlocks.
129143
*/
130-
if (rt_index == parsetree->resultRelation)
144+
if (!forExecute)
145+
lockmode = AccessShareLock;
146+
else if (rt_index == parsetree->resultRelation)
131147
lockmode = RowExclusiveLock;
132148
else if (get_rowmark(parsetree, rt_index))
133149
lockmode = RowShareLock;
@@ -206,7 +222,7 @@ AcquireRewriteLocks(Query *parsetree)
206222
* The subquery RTE itself is all right, but we have to
207223
* recurse to process the represented subquery.
208224
*/
209-
AcquireRewriteLocks(rte->subquery);
225+
AcquireRewriteLocks(rte->subquery, forExecute);
210226
break;
211227

212228
default:
@@ -220,23 +236,23 @@ AcquireRewriteLocks(Query *parsetree)
220236
{
221237
CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
222238

223-
AcquireRewriteLocks((Query *) cte->ctequery);
239+
AcquireRewriteLocks((Query *) cte->ctequery, forExecute);
224240
}
225241

226242
/*
227243
* Recurse into sublink subqueries, too. But we already did the ones in
228244
* the rtable and cteList.
229245
*/
230246
if (parsetree->hasSubLinks)
231-
query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL,
247+
query_tree_walker(parsetree, acquireLocksOnSubLinks, &context,
232248
QTW_IGNORE_RC_SUBQUERIES);
233249
}
234250

235251
/*
236252
* Walker to find sublink subqueries for AcquireRewriteLocks
237253
*/
238254
static bool
239-
acquireLocksOnSubLinks(Node *node, void *context)
255+
acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context)
240256
{
241257
if (node == NULL)
242258
return false;
@@ -245,7 +261,7 @@ acquireLocksOnSubLinks(Node *node, void *context)
245261
SubLink *sub = (SubLink *) node;
246262

247263
/* Do what we came for */
248-
AcquireRewriteLocks((Query *) sub->subselect);
264+
AcquireRewriteLocks((Query *) sub->subselect, context->for_execute);
249265
/* Fall through to process lefthand args of SubLink */
250266
}
251267

@@ -287,6 +303,9 @@ rewriteRuleAction(Query *parsetree,
287303
int rt_length;
288304
Query *sub_action;
289305
Query **sub_action_ptr;
306+
acquireLocksOnSubLinks_context context;
307+
308+
context.for_execute = true;
290309

291310
/*
292311
* Make modifiable copies of rule action and qual (what we're passed are
@@ -298,8 +317,8 @@ rewriteRuleAction(Query *parsetree,
298317
/*
299318
* Acquire necessary locks and fix any deleted JOIN RTE entries.
300319
*/
301-
AcquireRewriteLocks(rule_action);
302-
(void) acquireLocksOnSubLinks(rule_qual, NULL);
320+
AcquireRewriteLocks(rule_action, true);
321+
(void) acquireLocksOnSubLinks(rule_qual, &context);
303322

304323
current_varno = rt_index;
305324
rt_length = list_length(parsetree->rtable);
@@ -1154,7 +1173,7 @@ ApplyRetrieveRule(Query *parsetree,
11541173
*/
11551174
rule_action = copyObject(linitial(rule->actions));
11561175

1157-
AcquireRewriteLocks(rule_action);
1176+
AcquireRewriteLocks(rule_action, true);
11581177

11591178
/*
11601179
* Recursively expand any view references inside the view.
@@ -1465,14 +1484,17 @@ CopyAndAddInvertedQual(Query *parsetree,
14651484
{
14661485
/* Don't scribble on the passed qual (it's in the relcache!) */
14671486
Node *new_qual = (Node *) copyObject(rule_qual);
1487+
acquireLocksOnSubLinks_context context;
1488+
1489+
context.for_execute = true;
14681490

14691491
/*
14701492
* In case there are subqueries in the qual, acquire necessary locks and
14711493
* fix any deleted JOIN RTE entries. (This is somewhat redundant with
14721494
* rewriteRuleAction, but not entirely ... consider restructuring so that
14731495
* we only need to process the qual this way once.)
14741496
*/
1475-
(void) acquireLocksOnSubLinks(new_qual, NULL);
1497+
(void) acquireLocksOnSubLinks(new_qual, &context);
14761498

14771499
/* Fix references to OLD */
14781500
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
@@ -2126,7 +2126,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
21262126
query = getInsertSelectQuery(query, NULL);
21272127

21282128
/* Must acquire locks right away; see notes in get_query_def() */
2129-
AcquireRewriteLocks(query);
2129+
AcquireRewriteLocks(query, false);
21302130

21312131
context.buf = buf;
21322132
context.namespaces = list_make1(&dpns);
@@ -2270,8 +2270,11 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
22702270
* relations, and fix up deleted columns in JOIN RTEs. This ensures
22712271
* consistent results. Note we assume it's OK to scribble on the passed
22722272
* querytree!
2273+
*
2274+
* We are only deparsing the query (we are not about to execute it), so we
2275+
* only need AccessShareLock on the relations it mentions.
22732276
*/
2274-
AcquireRewriteLocks(query);
2277+
AcquireRewriteLocks(query, false);
22752278

22762279
context.buf = buf;
22772280
context.namespaces = lcons(&dpns, list_copy(parentnamespace));

src/include/rewrite/rewriteHandler.h

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

2020
extern List *QueryRewrite(Query *parsetree);
21-
extern void AcquireRewriteLocks(Query *parsetree);
21+
extern void AcquireRewriteLocks(Query *parsetree, bool forExecute);
2222
extern Node *build_column_default(Relation rel, int attrno);
2323

2424
#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