23
23
#include "utils/acl.h"
24
24
#include "utils/lsyscache.h"
25
25
#include "utils/syscache.h"
26
+ #include "rewrite/rewriteHandler.h"
27
+ #include "access/heapam.h"
28
+ #include "nodes/nodeFuncs.h"
26
29
27
- static void LockTableRecurse (Oid reloid , LOCKMODE lockmode , bool nowait );
28
- static AclResult LockTableAclCheck (Oid relid , LOCKMODE lockmode );
30
+ static void LockTableRecurse (Oid reloid , LOCKMODE lockmode , bool nowait , Oid userid );
31
+ static AclResult LockTableAclCheck (Oid relid , LOCKMODE lockmode , Oid userid );
29
32
static void RangeVarCallbackForLockTable (const RangeVar * rv , Oid relid ,
30
33
Oid oldrelid , void * arg );
34
+ static void LockViewRecurse (Oid reloid , Oid root_reloid , LOCKMODE lockmode , bool nowait );
31
35
32
36
/*
33
37
* LOCK TABLE
@@ -62,8 +66,10 @@ LockTableCommand(LockStmt *lockstmt)
62
66
RangeVarCallbackForLockTable ,
63
67
(void * ) & lockstmt -> mode );
64
68
65
- if (recurse )
66
- LockTableRecurse (reloid , lockstmt -> mode , lockstmt -> nowait );
69
+ if (get_rel_relkind (reloid ) == RELKIND_VIEW )
70
+ LockViewRecurse (reloid , reloid , lockstmt -> mode , lockstmt -> nowait );
71
+ else if (recurse )
72
+ LockTableRecurse (reloid , lockstmt -> mode , lockstmt -> nowait , GetUserId ());
67
73
}
68
74
}
69
75
@@ -86,15 +92,17 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
86
92
return ; /* woops, concurrently dropped; no permissions
87
93
* check */
88
94
89
- /* Currently, we only allow plain tables to be locked */
90
- if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE )
95
+
96
+ /* Currently, we only allow plain tables or views to be locked */
97
+ if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE &&
98
+ relkind != RELKIND_VIEW )
91
99
ereport (ERROR ,
92
100
(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
93
- errmsg ("\"%s\" is not a table" ,
101
+ errmsg ("\"%s\" is not a table or a view " ,
94
102
rv -> relname )));
95
103
96
104
/* Check permissions. */
97
- aclresult = LockTableAclCheck (relid , lockmode );
105
+ aclresult = LockTableAclCheck (relid , lockmode , GetUserId () );
98
106
if (aclresult != ACLCHECK_OK )
99
107
aclcheck_error (aclresult , get_relkind_objtype (get_rel_relkind (relid )), rv -> relname );
100
108
}
@@ -107,7 +115,7 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
107
115
* multiply-inheriting children more than once, but that's no problem.
108
116
*/
109
117
static void
110
- LockTableRecurse (Oid reloid , LOCKMODE lockmode , bool nowait )
118
+ LockTableRecurse (Oid reloid , LOCKMODE lockmode , bool nowait , Oid userid )
111
119
{
112
120
List * children ;
113
121
ListCell * lc ;
@@ -120,7 +128,7 @@ LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
120
128
AclResult aclresult ;
121
129
122
130
/* Check permissions before acquiring the lock. */
123
- aclresult = LockTableAclCheck (childreloid , lockmode );
131
+ aclresult = LockTableAclCheck (childreloid , lockmode , userid );
124
132
if (aclresult != ACLCHECK_OK )
125
133
{
126
134
char * relname = get_rel_name (childreloid );
@@ -157,15 +165,120 @@ LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
157
165
continue ;
158
166
}
159
167
160
- LockTableRecurse (childreloid , lockmode , nowait );
168
+ LockTableRecurse (childreloid , lockmode , nowait , userid );
161
169
}
162
170
}
163
171
172
+ /*
173
+ * Apply LOCK TABLE recursively over a view
174
+ *
175
+ * All tables and views appearing in the view definition query are locked
176
+ * recursively with the same lock mode.
177
+ */
178
+
179
+ typedef struct
180
+ {
181
+ Oid root_reloid ;
182
+ LOCKMODE lockmode ;
183
+ bool nowait ;
184
+ Oid viewowner ;
185
+ Oid viewoid ;
186
+ } LockViewRecurse_context ;
187
+
188
+ static bool
189
+ LockViewRecurse_walker (Node * node , LockViewRecurse_context * context )
190
+ {
191
+ if (node == NULL )
192
+ return false;
193
+
194
+ if (IsA (node , Query ))
195
+ {
196
+ Query * query = (Query * ) node ;
197
+ ListCell * rtable ;
198
+
199
+ foreach (rtable , query -> rtable )
200
+ {
201
+ RangeTblEntry * rte = lfirst (rtable );
202
+ AclResult aclresult ;
203
+
204
+ Oid relid = rte -> relid ;
205
+ char relkind = rte -> relkind ;
206
+ char * relname = get_rel_name (relid );
207
+
208
+ /* The OLD and NEW placeholder entries in the view's rtable are skipped. */
209
+ if (relid == context -> viewoid &&
210
+ (!strcmp (rte -> eref -> aliasname , "old" ) || !strcmp (rte -> eref -> aliasname , "new" )))
211
+ continue ;
212
+
213
+ /* Currently, we only allow plain tables or views to be locked. */
214
+ if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE &&
215
+ relkind != RELKIND_VIEW )
216
+ continue ;
217
+
218
+ /* Check infinite recursion in the view definition. */
219
+ if (relid == context -> root_reloid )
220
+ ereport (ERROR ,
221
+ (errcode (ERRCODE_INVALID_OBJECT_DEFINITION ),
222
+ errmsg ("infinite recursion detected in rules for relation \"%s\"" ,
223
+ get_rel_name (context -> root_reloid ))));
224
+
225
+ /* Check permissions with the view owner's privilege. */
226
+ aclresult = LockTableAclCheck (relid , context -> lockmode , context -> viewowner );
227
+ if (aclresult != ACLCHECK_OK )
228
+ aclcheck_error (aclresult , get_relkind_objtype (relkind ), relname );
229
+
230
+ /* We have enough rights to lock the relation; do so. */
231
+ if (!context -> nowait )
232
+ LockRelationOid (relid , context -> lockmode );
233
+ else if (!ConditionalLockRelationOid (relid , context -> lockmode ))
234
+ ereport (ERROR ,
235
+ (errcode (ERRCODE_LOCK_NOT_AVAILABLE ),
236
+ errmsg ("could not obtain lock on relation \"%s\"" ,
237
+ relname )));
238
+
239
+ if (relkind == RELKIND_VIEW )
240
+ LockViewRecurse (relid , context -> root_reloid , context -> lockmode , context -> nowait );
241
+ else if (rte -> inh )
242
+ LockTableRecurse (relid , context -> lockmode , context -> nowait , context -> viewowner );
243
+ }
244
+
245
+ return query_tree_walker (query ,
246
+ LockViewRecurse_walker ,
247
+ context ,
248
+ QTW_IGNORE_JOINALIASES );
249
+ }
250
+
251
+ return expression_tree_walker (node ,
252
+ LockViewRecurse_walker ,
253
+ context );
254
+ }
255
+
256
+ static void
257
+ LockViewRecurse (Oid reloid , Oid root_reloid , LOCKMODE lockmode , bool nowait )
258
+ {
259
+ LockViewRecurse_context context ;
260
+
261
+ Relation view ;
262
+ Query * viewquery ;
263
+
264
+ view = heap_open (reloid , NoLock );
265
+ viewquery = get_view_query (view );
266
+ heap_close (view , NoLock );
267
+
268
+ context .root_reloid = root_reloid ;
269
+ context .lockmode = lockmode ;
270
+ context .nowait = nowait ;
271
+ context .viewowner = view -> rd_rel -> relowner ;
272
+ context .viewoid = reloid ;
273
+
274
+ LockViewRecurse_walker ((Node * ) viewquery , & context );
275
+ }
276
+
164
277
/*
165
278
* Check whether the current user is permitted to lock this relation.
166
279
*/
167
280
static AclResult
168
- LockTableAclCheck (Oid reloid , LOCKMODE lockmode )
281
+ LockTableAclCheck (Oid reloid , LOCKMODE lockmode , Oid userid )
169
282
{
170
283
AclResult aclresult ;
171
284
AclMode aclmask ;
@@ -178,7 +291,7 @@ LockTableAclCheck(Oid reloid, LOCKMODE lockmode)
178
291
else
179
292
aclmask = ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE ;
180
293
181
- aclresult = pg_class_aclcheck (reloid , GetUserId () , aclmask );
294
+ aclresult = pg_class_aclcheck (reloid , userid , aclmask );
182
295
183
296
return aclresult ;
184
297
}
0 commit comments