@@ -36,7 +36,13 @@ typedef struct rewrite_event
36
36
CmdType event ;/* type of rule being fired */
37
37
}rewrite_event ;
38
38
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 );
40
46
static Query * rewriteRuleAction (Query * parsetree ,
41
47
Query * rule_action ,
42
48
Node * rule_qual ,
@@ -66,9 +72,19 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
66
72
* These locks will ensure that the relation schemas don't change under us
67
73
* while we are rewriting and planning the query.
68
74
*
69
- * forUpdatePushedDown indicates that a pushed-down FOR UPDATE/SHARE applies
70
- * to the current subquery, requiring all rels to be opened with RowShareLock.
71
- * This should always be false at the start of the recursion.
75
+ * forExecute indicates that the query is about to be executed.
76
+ * If so, we'll acquire RowExclusiveLock on the query's resultRelation,
77
+ * RowShareLock on any relation accessed FOR UPDATE/SHARE, and
78
+ * AccessShareLock on all other relations mentioned.
79
+ *
80
+ * If forExecute is false, AccessShareLock is acquired on all relations.
81
+ * This case is suitable for ruleutils.c, for example, where we only need
82
+ * schema stability and we don't intend to actually modify any relations.
83
+ *
84
+ * forUpdatePushedDown indicates that a pushed-down FOR UPDATE/SHARE
85
+ * applies to the current subquery, requiring all rels to be opened with at
86
+ * least RowShareLock. This should always be false at the top of the
87
+ * recursion. This flag is ignored if forExecute is false.
72
88
*
73
89
* A secondary purpose of this routine is to fix up JOIN RTE references to
74
90
* dropped columns (see details below). Because the RTEs are modified in
@@ -96,10 +112,15 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
96
112
* construction of a nested join was O(N^2) in the nesting depth.)
97
113
*/
98
114
void
99
- AcquireRewriteLocks (Query * parsetree ,bool forUpdatePushedDown )
115
+ AcquireRewriteLocks (Query * parsetree ,
116
+ bool forExecute ,
117
+ bool forUpdatePushedDown )
100
118
{
101
119
ListCell * l ;
102
120
int rt_index ;
121
+ acquireLocksOnSubLinks_context context ;
122
+
123
+ context .for_execute = forExecute ;
103
124
104
125
/*
105
126
* First, process RTEs of the current query level.
@@ -125,14 +146,12 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
125
146
* release it until end of transaction. This protects the
126
147
* rewriter and planner against schema changes mid-query.
127
148
*
128
- * If the relation is the query's result relation, then we
129
- * need RowExclusiveLock. Otherwise, check to see if the
130
- * relation is accessed FOR UPDATE/SHARE or not. We can't
131
- * just grab AccessShareLock because then the executor would
132
- * be trying to upgrade the lock, leading to possible
133
- * deadlocks.
149
+ * Assuming forExecute is true, this logic must match what the
150
+ * executor will do, else we risk lock-upgrade deadlocks.
134
151
*/
135
- if (rt_index == parsetree -> resultRelation )
152
+ if (!forExecute )
153
+ lockmode = AccessShareLock ;
154
+ else if (rt_index == parsetree -> resultRelation )
136
155
lockmode = RowExclusiveLock ;
137
156
else if (forUpdatePushedDown ||
138
157
get_parse_rowmark (parsetree ,rt_index )!= NULL )
@@ -213,6 +232,7 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
213
232
* recurse to process the represented subquery.
214
233
*/
215
234
AcquireRewriteLocks (rte -> subquery ,
235
+ forExecute ,
216
236
(forUpdatePushedDown ||
217
237
get_parse_rowmark (parsetree ,rt_index )!= NULL ));
218
238
break ;
@@ -228,23 +248,23 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
228
248
{
229
249
CommonTableExpr * cte = (CommonTableExpr * )lfirst (l );
230
250
231
- AcquireRewriteLocks ((Query * )cte -> ctequery , false);
251
+ AcquireRewriteLocks ((Query * )cte -> ctequery ,forExecute , false);
232
252
}
233
253
234
254
/*
235
255
* Recurse into sublink subqueries, too. But we already did the ones in
236
256
* the rtable and cteList.
237
257
*/
238
258
if (parsetree -> hasSubLinks )
239
- query_tree_walker (parsetree ,acquireLocksOnSubLinks ,NULL ,
259
+ query_tree_walker (parsetree ,acquireLocksOnSubLinks ,& context ,
240
260
QTW_IGNORE_RC_SUBQUERIES );
241
261
}
242
262
243
263
/*
244
264
* Walker to find sublink subqueries for AcquireRewriteLocks
245
265
*/
246
266
static bool
247
- acquireLocksOnSubLinks (Node * node ,void * context )
267
+ acquireLocksOnSubLinks (Node * node ,acquireLocksOnSubLinks_context * context )
248
268
{
249
269
if (node == NULL )
250
270
return false;
@@ -253,7 +273,9 @@ acquireLocksOnSubLinks(Node *node, void *context)
253
273
SubLink * sub = (SubLink * )node ;
254
274
255
275
/* Do what we came for */
256
- AcquireRewriteLocks ((Query * )sub -> subselect , false);
276
+ AcquireRewriteLocks ((Query * )sub -> subselect ,
277
+ context -> for_execute ,
278
+ false);
257
279
/* Fall through to process lefthand args of SubLink */
258
280
}
259
281
@@ -295,6 +317,9 @@ rewriteRuleAction(Query *parsetree,
295
317
int rt_length ;
296
318
Query * sub_action ;
297
319
Query * * sub_action_ptr ;
320
+ acquireLocksOnSubLinks_context context ;
321
+
322
+ context .for_execute = true;
298
323
299
324
/*
300
325
* Make modifiable copies of rule action and qual (what we're passed are
@@ -306,8 +331,8 @@ rewriteRuleAction(Query *parsetree,
306
331
/*
307
332
* Acquire necessary locks and fix any deleted JOIN RTE entries.
308
333
*/
309
- AcquireRewriteLocks (rule_action , false);
310
- (void )acquireLocksOnSubLinks (rule_qual ,NULL );
334
+ AcquireRewriteLocks (rule_action ,true, false);
335
+ (void )acquireLocksOnSubLinks (rule_qual ,& context );
311
336
312
337
current_varno = rt_index ;
313
338
rt_length = list_length (parsetree -> rtable );
@@ -1170,7 +1195,7 @@ ApplyRetrieveRule(Query *parsetree,
1170
1195
*/
1171
1196
rule_action = copyObject (linitial (rule -> actions ));
1172
1197
1173
- AcquireRewriteLocks (rule_action ,forUpdatePushedDown );
1198
+ AcquireRewriteLocks (rule_action ,true, forUpdatePushedDown );
1174
1199
1175
1200
/*
1176
1201
* Recursively expand any view references inside the view.
@@ -1479,14 +1504,17 @@ CopyAndAddInvertedQual(Query *parsetree,
1479
1504
{
1480
1505
/* Don't scribble on the passed qual (it's in the relcache!) */
1481
1506
Node * new_qual = (Node * )copyObject (rule_qual );
1507
+ acquireLocksOnSubLinks_context context ;
1508
+
1509
+ context .for_execute = true;
1482
1510
1483
1511
/*
1484
1512
* In case there are subqueries in the qual, acquire necessary locks and
1485
1513
* fix any deleted JOIN RTE entries. (This is somewhat redundant with
1486
1514
* rewriteRuleAction, but not entirely ... consider restructuring so that
1487
1515
* we only need to process the qual this way once.)
1488
1516
*/
1489
- (void )acquireLocksOnSubLinks (new_qual ,NULL );
1517
+ (void )acquireLocksOnSubLinks (new_qual ,& context );
1490
1518
1491
1519
/* Fix references to OLD */
1492
1520
ChangeVarNodes (new_qual ,PRS2_OLD_VARNO ,rt_index ,0 );