@@ -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 ,
@@ -71,9 +77,19 @@ static Bitmapset *adjust_view_column_set(Bitmapset *cols, List *targetlist);
71
77
* These locks will ensure that the relation schemas don't change under us
72
78
* while we are rewriting and planning the query.
73
79
*
74
- * forUpdatePushedDown indicates that a pushed-down FOR [KEY] UPDATE/SHARE applies
75
- * to the current subquery, requiring all rels to be opened with RowShareLock.
76
- * This should always be false at the start of the recursion.
80
+ * forExecute indicates that the query is about to be executed.
81
+ * If so, we'll acquire RowExclusiveLock on the query's resultRelation,
82
+ * RowShareLock on any relation accessed FOR [KEY] UPDATE/SHARE, and
83
+ * AccessShareLock on all other relations mentioned.
84
+ *
85
+ * If forExecute is false, AccessShareLock is acquired on all relations.
86
+ * This case is suitable for ruleutils.c, for example, where we only need
87
+ * schema stability and we don't intend to actually modify any relations.
88
+ *
89
+ * forUpdatePushedDown indicates that a pushed-down FOR [KEY] UPDATE/SHARE
90
+ * applies to the current subquery, requiring all rels to be opened with at
91
+ * least RowShareLock. This should always be false at the top of the
92
+ * recursion. This flag is ignored if forExecute is false.
77
93
*
78
94
* A secondary purpose of this routine is to fix up JOIN RTE references to
79
95
* dropped columns (see details below). Because the RTEs are modified in
@@ -101,10 +117,15 @@ static Bitmapset *adjust_view_column_set(Bitmapset *cols, List *targetlist);
101
117
* construction of a nested join was O(N^2) in the nesting depth.)
102
118
*/
103
119
void
104
- AcquireRewriteLocks (Query * parsetree , bool forUpdatePushedDown )
120
+ AcquireRewriteLocks (Query * parsetree ,
121
+ bool forExecute ,
122
+ bool forUpdatePushedDown )
105
123
{
106
124
ListCell * l ;
107
125
int rt_index ;
126
+ acquireLocksOnSubLinks_context context ;
127
+
128
+ context .for_execute = forExecute ;
108
129
109
130
/*
110
131
* First, process RTEs of the current query level.
@@ -130,14 +151,12 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
130
151
* release it until end of transaction. This protects the
131
152
* rewriter and planner against schema changes mid-query.
132
153
*
133
- * If the relation is the query's result relation, then we
134
- * need RowExclusiveLock. Otherwise, check to see if the
135
- * relation is accessed FOR [KEY] UPDATE/SHARE or not. We
136
- * can't just grab AccessShareLock because then the executor
137
- * would be trying to upgrade the lock, leading to possible
138
- * deadlocks.
154
+ * Assuming forExecute is true, this logic must match what the
155
+ * executor will do, else we risk lock-upgrade deadlocks.
139
156
*/
140
- if (rt_index == parsetree -> resultRelation )
157
+ if (!forExecute )
158
+ lockmode = AccessShareLock ;
159
+ else if (rt_index == parsetree -> resultRelation )
141
160
lockmode = RowExclusiveLock ;
142
161
else if (forUpdatePushedDown ||
143
162
get_parse_rowmark (parsetree , rt_index ) != NULL )
@@ -225,6 +244,7 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
225
244
* recurse to process the represented subquery.
226
245
*/
227
246
AcquireRewriteLocks (rte -> subquery ,
247
+ forExecute ,
228
248
(forUpdatePushedDown ||
229
249
get_parse_rowmark (parsetree , rt_index ) != NULL ));
230
250
break ;
@@ -240,23 +260,23 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
240
260
{
241
261
CommonTableExpr * cte = (CommonTableExpr * ) lfirst (l );
242
262
243
- AcquireRewriteLocks ((Query * ) cte -> ctequery , false);
263
+ AcquireRewriteLocks ((Query * ) cte -> ctequery , forExecute , false);
244
264
}
245
265
246
266
/*
247
267
* Recurse into sublink subqueries, too. But we already did the ones in
248
268
* the rtable and cteList.
249
269
*/
250
270
if (parsetree -> hasSubLinks )
251
- query_tree_walker (parsetree , acquireLocksOnSubLinks , NULL ,
271
+ query_tree_walker (parsetree , acquireLocksOnSubLinks , & context ,
252
272
QTW_IGNORE_RC_SUBQUERIES );
253
273
}
254
274
255
275
/*
256
276
* Walker to find sublink subqueries for AcquireRewriteLocks
257
277
*/
258
278
static bool
259
- acquireLocksOnSubLinks (Node * node , void * context )
279
+ acquireLocksOnSubLinks (Node * node , acquireLocksOnSubLinks_context * context )
260
280
{
261
281
if (node == NULL )
262
282
return false;
@@ -265,7 +285,9 @@ acquireLocksOnSubLinks(Node *node, void *context)
265
285
SubLink * sub = (SubLink * ) node ;
266
286
267
287
/* Do what we came for */
268
- AcquireRewriteLocks ((Query * ) sub -> subselect , false);
288
+ AcquireRewriteLocks ((Query * ) sub -> subselect ,
289
+ context -> for_execute ,
290
+ false);
269
291
/* Fall through to process lefthand args of SubLink */
270
292
}
271
293
@@ -307,6 +329,9 @@ rewriteRuleAction(Query *parsetree,
307
329
int rt_length ;
308
330
Query * sub_action ;
309
331
Query * * sub_action_ptr ;
332
+ acquireLocksOnSubLinks_context context ;
333
+
334
+ context .for_execute = true;
310
335
311
336
/*
312
337
* Make modifiable copies of rule action and qual (what we're passed are
@@ -318,8 +343,8 @@ rewriteRuleAction(Query *parsetree,
318
343
/*
319
344
* Acquire necessary locks and fix any deleted JOIN RTE entries.
320
345
*/
321
- AcquireRewriteLocks (rule_action , false);
322
- (void ) acquireLocksOnSubLinks (rule_qual , NULL );
346
+ AcquireRewriteLocks (rule_action , true, false);
347
+ (void ) acquireLocksOnSubLinks (rule_qual , & context );
323
348
324
349
current_varno = rt_index ;
325
350
rt_length = list_length (parsetree -> rtable );
@@ -1389,7 +1414,7 @@ ApplyRetrieveRule(Query *parsetree,
1389
1414
*/
1390
1415
rule_action = copyObject (linitial (rule -> actions ));
1391
1416
1392
- AcquireRewriteLocks (rule_action , forUpdatePushedDown );
1417
+ AcquireRewriteLocks (rule_action , true, forUpdatePushedDown );
1393
1418
1394
1419
/*
1395
1420
* Recursively expand any view references inside the view.
@@ -1712,14 +1737,17 @@ CopyAndAddInvertedQual(Query *parsetree,
1712
1737
{
1713
1738
/* Don't scribble on the passed qual (it's in the relcache!) */
1714
1739
Node * new_qual = (Node * ) copyObject (rule_qual );
1740
+ acquireLocksOnSubLinks_context context ;
1741
+
1742
+ context .for_execute = true;
1715
1743
1716
1744
/*
1717
1745
* In case there are subqueries in the qual, acquire necessary locks and
1718
1746
* fix any deleted JOIN RTE entries. (This is somewhat redundant with
1719
1747
* rewriteRuleAction, but not entirely ... consider restructuring so that
1720
1748
* we only need to process the qual this way once.)
1721
1749
*/
1722
- (void ) acquireLocksOnSubLinks (new_qual , NULL );
1750
+ (void ) acquireLocksOnSubLinks (new_qual , & context );
1723
1751
1724
1752
/* Fix references to OLD */
1725
1753
ChangeVarNodes (new_qual , PRS2_OLD_VARNO , rt_index , 0 );
0 commit comments