@@ -37,7 +37,13 @@ typedef struct rewrite_event
37
37
CmdType event ; /* type of rule being fired */
38
38
} rewrite_event ;
39
39
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 );
41
47
static Query * rewriteRuleAction (Query * parsetree ,
42
48
Query * rule_action ,
43
49
Node * rule_qual ,
@@ -69,9 +75,19 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
69
75
* These locks will ensure that the relation schemas don't change under us
70
76
* while we are rewriting and planning the query.
71
77
*
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.
75
91
*
76
92
* A secondary purpose of this routine is to fix up JOIN RTE references to
77
93
* dropped columns (see details below). Because the RTEs are modified in
@@ -99,10 +115,15 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
99
115
* construction of a nested join was O(N^2) in the nesting depth.)
100
116
*/
101
117
void
102
- AcquireRewriteLocks (Query * parsetree , bool forUpdatePushedDown )
118
+ AcquireRewriteLocks (Query * parsetree ,
119
+ bool forExecute ,
120
+ bool forUpdatePushedDown )
103
121
{
104
122
ListCell * l ;
105
123
int rt_index ;
124
+ acquireLocksOnSubLinks_context context ;
125
+
126
+ context .for_execute = forExecute ;
106
127
107
128
/*
108
129
* First, process RTEs of the current query level.
@@ -128,14 +149,12 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
128
149
* release it until end of transaction. This protects the
129
150
* rewriter and planner against schema changes mid-query.
130
151
*
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.
137
154
*/
138
- if (rt_index == parsetree -> resultRelation )
155
+ if (!forExecute )
156
+ lockmode = AccessShareLock ;
157
+ else if (rt_index == parsetree -> resultRelation )
139
158
lockmode = RowExclusiveLock ;
140
159
else if (forUpdatePushedDown ||
141
160
get_parse_rowmark (parsetree , rt_index ) != NULL )
@@ -223,6 +242,7 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
223
242
* recurse to process the represented subquery.
224
243
*/
225
244
AcquireRewriteLocks (rte -> subquery ,
245
+ forExecute ,
226
246
(forUpdatePushedDown ||
227
247
get_parse_rowmark (parsetree , rt_index ) != NULL ));
228
248
break ;
@@ -238,23 +258,23 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
238
258
{
239
259
CommonTableExpr * cte = (CommonTableExpr * ) lfirst (l );
240
260
241
- AcquireRewriteLocks ((Query * ) cte -> ctequery , false);
261
+ AcquireRewriteLocks ((Query * ) cte -> ctequery , forExecute , false);
242
262
}
243
263
244
264
/*
245
265
* Recurse into sublink subqueries, too. But we already did the ones in
246
266
* the rtable and cteList.
247
267
*/
248
268
if (parsetree -> hasSubLinks )
249
- query_tree_walker (parsetree , acquireLocksOnSubLinks , NULL ,
269
+ query_tree_walker (parsetree , acquireLocksOnSubLinks , & context ,
250
270
QTW_IGNORE_RC_SUBQUERIES );
251
271
}
252
272
253
273
/*
254
274
* Walker to find sublink subqueries for AcquireRewriteLocks
255
275
*/
256
276
static bool
257
- acquireLocksOnSubLinks (Node * node , void * context )
277
+ acquireLocksOnSubLinks (Node * node , acquireLocksOnSubLinks_context * context )
258
278
{
259
279
if (node == NULL )
260
280
return false;
@@ -263,7 +283,9 @@ acquireLocksOnSubLinks(Node *node, void *context)
263
283
SubLink * sub = (SubLink * ) node ;
264
284
265
285
/* Do what we came for */
266
- AcquireRewriteLocks ((Query * ) sub -> subselect , false);
286
+ AcquireRewriteLocks ((Query * ) sub -> subselect ,
287
+ context -> for_execute ,
288
+ false);
267
289
/* Fall through to process lefthand args of SubLink */
268
290
}
269
291
@@ -305,6 +327,9 @@ rewriteRuleAction(Query *parsetree,
305
327
int rt_length ;
306
328
Query * sub_action ;
307
329
Query * * sub_action_ptr ;
330
+ acquireLocksOnSubLinks_context context ;
331
+
332
+ context .for_execute = true;
308
333
309
334
/*
310
335
* Make modifiable copies of rule action and qual (what we're passed are
@@ -316,8 +341,8 @@ rewriteRuleAction(Query *parsetree,
316
341
/*
317
342
* Acquire necessary locks and fix any deleted JOIN RTE entries.
318
343
*/
319
- AcquireRewriteLocks (rule_action , false);
320
- (void ) acquireLocksOnSubLinks (rule_qual , NULL );
344
+ AcquireRewriteLocks (rule_action , true, false);
345
+ (void ) acquireLocksOnSubLinks (rule_qual , & context );
321
346
322
347
current_varno = rt_index ;
323
348
rt_length = list_length (parsetree -> rtable );
@@ -1367,7 +1392,7 @@ ApplyRetrieveRule(Query *parsetree,
1367
1392
*/
1368
1393
rule_action = copyObject (linitial (rule -> actions ));
1369
1394
1370
- AcquireRewriteLocks (rule_action , forUpdatePushedDown );
1395
+ AcquireRewriteLocks (rule_action , true, forUpdatePushedDown );
1371
1396
1372
1397
/*
1373
1398
* Recursively expand any view references inside the view.
@@ -1690,14 +1715,17 @@ CopyAndAddInvertedQual(Query *parsetree,
1690
1715
{
1691
1716
/* Don't scribble on the passed qual (it's in the relcache!) */
1692
1717
Node * new_qual = (Node * ) copyObject (rule_qual );
1718
+ acquireLocksOnSubLinks_context context ;
1719
+
1720
+ context .for_execute = true;
1693
1721
1694
1722
/*
1695
1723
* In case there are subqueries in the qual, acquire necessary locks and
1696
1724
* fix any deleted JOIN RTE entries. (This is somewhat redundant with
1697
1725
* rewriteRuleAction, but not entirely ... consider restructuring so that
1698
1726
* we only need to process the qual this way once.)
1699
1727
*/
1700
- (void ) acquireLocksOnSubLinks (new_qual , NULL );
1728
+ (void ) acquireLocksOnSubLinks (new_qual , & context );
1701
1729
1702
1730
/* Fix references to OLD */
1703
1731
ChangeVarNodes (new_qual , PRS2_OLD_VARNO , rt_index , 0 );
0 commit comments