2323#include "utils/acl.h"
2424#include "utils/lsyscache.h"
2525#include "utils/syscache.h"
26+ #include "rewrite/rewriteHandler.h"
27+ #include "access/heapam.h"
28+ #include "nodes/nodeFuncs.h"
2629
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 );
2932static void RangeVarCallbackForLockTable (const RangeVar * rv ,Oid relid ,
3033Oid oldrelid ,void * arg );
34+ static void LockViewRecurse (Oid reloid ,Oid root_reloid ,LOCKMODE lockmode ,bool nowait );
3135
3236/*
3337 * LOCK TABLE
@@ -62,8 +66,10 @@ LockTableCommand(LockStmt *lockstmt)
6266RangeVarCallbackForLockTable ,
6367 (void * )& lockstmt -> mode );
6468
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 ());
6773}
6874}
6975
@@ -86,15 +92,17 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
8692return ;/* woops, concurrently dropped; no permissions
8793 * check */
8894
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 )
9199ereport (ERROR ,
92100(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
93- errmsg ("\"%s\" is not a table" ,
101+ errmsg ("\"%s\" is not a table or a view " ,
94102rv -> relname )));
95103
96104/* Check permissions. */
97- aclresult = LockTableAclCheck (relid ,lockmode );
105+ aclresult = LockTableAclCheck (relid ,lockmode , GetUserId () );
98106if (aclresult != ACLCHECK_OK )
99107aclcheck_error (aclresult ,get_relkind_objtype (get_rel_relkind (relid )),rv -> relname );
100108}
@@ -107,7 +115,7 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
107115 * multiply-inheriting children more than once, but that's no problem.
108116 */
109117static void
110- LockTableRecurse (Oid reloid ,LOCKMODE lockmode ,bool nowait )
118+ LockTableRecurse (Oid reloid ,LOCKMODE lockmode ,bool nowait , Oid userid )
111119{
112120List * children ;
113121ListCell * lc ;
@@ -120,7 +128,7 @@ LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
120128AclResult aclresult ;
121129
122130/* Check permissions before acquiring the lock. */
123- aclresult = LockTableAclCheck (childreloid ,lockmode );
131+ aclresult = LockTableAclCheck (childreloid ,lockmode , userid );
124132if (aclresult != ACLCHECK_OK )
125133{
126134char * relname = get_rel_name (childreloid );
@@ -157,15 +165,120 @@ LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
157165continue ;
158166}
159167
160- LockTableRecurse (childreloid ,lockmode ,nowait );
168+ LockTableRecurse (childreloid ,lockmode ,nowait , userid );
161169}
162170}
163171
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+
164277/*
165278 * Check whether the current user is permitted to lock this relation.
166279 */
167280static AclResult
168- LockTableAclCheck (Oid reloid ,LOCKMODE lockmode )
281+ LockTableAclCheck (Oid reloid ,LOCKMODE lockmode , Oid userid )
169282{
170283AclResult aclresult ;
171284AclMode aclmask ;
@@ -178,7 +291,7 @@ LockTableAclCheck(Oid reloid, LOCKMODE lockmode)
178291else
179292aclmask = ACL_UPDATE |ACL_DELETE |ACL_TRUNCATE ;
180293
181- aclresult = pg_class_aclcheck (reloid ,GetUserId () ,aclmask );
294+ aclresult = pg_class_aclcheck (reloid ,userid ,aclmask );
182295
183296return aclresult ;
184297}