PostgreSQL Source Code  git master
lockcmds.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * lockcmds.c
4  * LOCK command support code
5  *
6  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/commands/lockcmds.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "catalog/namespace.h"
18 #include "catalog/pg_inherits.h"
19 #include "commands/lockcmds.h"
20 #include "miscadmin.h"
21 #include "parser/parse_clause.h"
22 #include "storage/lmgr.h"
23 #include "utils/acl.h"
24 #include "utils/lsyscache.h"
25 #include "utils/syscache.h"
26 #include "rewrite/rewriteHandler.h"
27 #include "access/heapam.h"
28 #include "nodes/nodeFuncs.h"
29 
30 static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid userid);
31 static AclResult LockTableAclCheck(Oid relid, LOCKMODE lockmode, Oid userid);
32 static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid,
33  Oid oldrelid, void *arg);
34 static void LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views);
35 
36 /*
37  * LOCK TABLE
38  */
39 void
41 {
42  ListCell *p;
43 
44  /*---------
45  * During recovery we only accept these variations:
46  * LOCK TABLE foo IN ACCESS SHARE MODE
47  * LOCK TABLE foo IN ROW SHARE MODE
48  * LOCK TABLE foo IN ROW EXCLUSIVE MODE
49  * This test must match the restrictions defined in LockAcquireExtended()
50  *---------
51  */
52  if (lockstmt->mode > RowExclusiveLock)
53  PreventCommandDuringRecovery("LOCK TABLE");
54 
55  /*
56  * Iterate over the list and process the named relations one at a time
57  */
58  foreach(p, lockstmt->relations)
59  {
60  RangeVar *rv = (RangeVar *) lfirst(p);
61  bool recurse = rv->inh;
62  Oid reloid;
63 
64  reloid = RangeVarGetRelidExtended(rv, lockstmt->mode,
65  lockstmt->nowait ? RVR_NOWAIT : 0,
67  (void *) &lockstmt->mode);
68 
69  if (get_rel_relkind(reloid) == RELKIND_VIEW)
70  LockViewRecurse(reloid, lockstmt->mode, lockstmt->nowait, NIL);
71  else if (recurse)
72  LockTableRecurse(reloid, lockstmt->mode, lockstmt->nowait, GetUserId());
73  }
74 }
75 
76 /*
77  * Before acquiring a table lock on the named table, check whether we have
78  * permission to do so.
79  */
80 static void
81 RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
82  void *arg)
83 {
84  LOCKMODE lockmode = *(LOCKMODE *) arg;
85  char relkind;
86  AclResult aclresult;
87 
88  if (!OidIsValid(relid))
89  return; /* doesn't exist, so no permissions check */
90  relkind = get_rel_relkind(relid);
91  if (!relkind)
92  return; /* woops, concurrently dropped; no permissions
93  * check */
94 
95  /* Currently, we only allow plain tables or views to be locked */
96  if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE &&
97  relkind != RELKIND_VIEW)
98  ereport(ERROR,
99  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
100  errmsg("\"%s\" is not a table or a view",
101  rv->relname)));
102 
103  /* Check permissions. */
104  aclresult = LockTableAclCheck(relid, lockmode, GetUserId());
105  if (aclresult != ACLCHECK_OK)
107 }
108 
109 /*
110  * Apply LOCK TABLE recursively over an inheritance tree
111  *
112  * We use find_inheritance_children not find_all_inheritors to avoid taking
113  * locks far in advance of checking privileges. This means we'll visit
114  * multiply-inheriting children more than once, but that's no problem.
115  */
116 static void
117 LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid userid)
118 {
119  List *children;
120  ListCell *lc;
121 
122  children = find_inheritance_children(reloid, NoLock);
123 
124  foreach(lc, children)
125  {
126  Oid childreloid = lfirst_oid(lc);
127  AclResult aclresult;
128 
129  /* Check permissions before acquiring the lock. */
130  aclresult = LockTableAclCheck(childreloid, lockmode, userid);
131  if (aclresult != ACLCHECK_OK)
132  {
133  char *relname = get_rel_name(childreloid);
134 
135  if (!relname)
136  continue; /* child concurrently dropped, just skip it */
137  aclcheck_error(aclresult, get_relkind_objtype(get_rel_relkind(childreloid)), relname);
138  }
139 
140  /* We have enough rights to lock the relation; do so. */
141  if (!nowait)
142  LockRelationOid(childreloid, lockmode);
143  else if (!ConditionalLockRelationOid(childreloid, lockmode))
144  {
145  /* try to throw error by name; relation could be deleted... */
146  char *relname = get_rel_name(childreloid);
147 
148  if (!relname)
149  continue; /* child concurrently dropped, just skip it */
150  ereport(ERROR,
151  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
152  errmsg("could not obtain lock on relation \"%s\"",
153  relname)));
154  }
155 
156  /*
157  * Even if we got the lock, child might have been concurrently
158  * dropped. If so, we can skip it.
159  */
160  if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(childreloid)))
161  {
162  /* Release useless lock */
163  UnlockRelationOid(childreloid, lockmode);
164  continue;
165  }
166 
167  LockTableRecurse(childreloid, lockmode, nowait, userid);
168  }
169 }
170 
171 /*
172  * Apply LOCK TABLE recursively over a view
173  *
174  * All tables and views appearing in the view definition query are locked
175  * recursively with the same lock mode.
176  */
177 
178 typedef struct
179 {
180  LOCKMODE lockmode; /* lock mode to use */
181  bool nowait; /* no wait mode */
182  Oid viewowner; /* view owner for checking the privilege */
183  Oid viewoid; /* OID of the view to be locked */
184  List *ancestor_views; /* OIDs of ancestor views */
186 
187 static bool
189 {
190  if (node == NULL)
191  return false;
192 
193  if (IsA(node, Query))
194  {
195  Query *query = (Query *) node;
196  ListCell *rtable;
197 
198  foreach(rtable, query->rtable)
199  {
200  RangeTblEntry *rte = lfirst(rtable);
201  AclResult aclresult;
202 
203  Oid relid = rte->relid;
204  char relkind = rte->relkind;
205  char *relname = get_rel_name(relid);
206 
207  /*
208  * The OLD and NEW placeholder entries in the view's rtable are
209  * skipped.
210  */
211  if (relid == context->viewoid &&
212  (!strcmp(rte->eref->aliasname, "old") || !strcmp(rte->eref->aliasname, "new")))
213  continue;
214 
215  /* Currently, we only allow plain tables or views to be locked. */
216  if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE &&
217  relkind != RELKIND_VIEW)
218  continue;
219 
220  /* Check infinite recursion in the view definition. */
221  if (list_member_oid(context->ancestor_views, relid))
222  ereport(ERROR,
223  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
224  errmsg("infinite recursion detected in rules for relation \"%s\"",
225  get_rel_name(relid))));
226 
227  /* Check permissions with the view owner's privilege. */
228  aclresult = LockTableAclCheck(relid, context->lockmode, context->viewowner);
229  if (aclresult != ACLCHECK_OK)
230  aclcheck_error(aclresult, get_relkind_objtype(relkind), relname);
231 
232  /* We have enough rights to lock the relation; do so. */
233  if (!context->nowait)
234  LockRelationOid(relid, context->lockmode);
235  else if (!ConditionalLockRelationOid(relid, context->lockmode))
236  ereport(ERROR,
237  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
238  errmsg("could not obtain lock on relation \"%s\"",
239  relname)));
240 
241  if (relkind == RELKIND_VIEW)
242  LockViewRecurse(relid, context->lockmode, context->nowait, context->ancestor_views);
243  else if (rte->inh)
244  LockTableRecurse(relid, context->lockmode, context->nowait, context->viewowner);
245  }
246 
247  return query_tree_walker(query,
249  context,
251  }
252 
253  return expression_tree_walker(node,
255  context);
256 }
257 
258 static void
259 LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views)
260 {
261  LockViewRecurse_context context;
262 
263  Relation view;
264  Query *viewquery;
265 
266  view = heap_open(reloid, NoLock);
267  viewquery = get_view_query(view);
268 
269  context.lockmode = lockmode;
270  context.nowait = nowait;
271  context.viewowner = view->rd_rel->relowner;
272  context.viewoid = reloid;
273  context.ancestor_views = lcons_oid(reloid, ancestor_views);
274 
275  LockViewRecurse_walker((Node *) viewquery, &context);
276 
277  ancestor_views = list_delete_oid(ancestor_views, reloid);
278 
279  heap_close(view, NoLock);
280 }
281 
282 /*
283  * Check whether the current user is permitted to lock this relation.
284  */
285 static AclResult
286 LockTableAclCheck(Oid reloid, LOCKMODE lockmode, Oid userid)
287 {
288  AclResult aclresult;
290 
291  /* Verify adequate privilege */
292  if (lockmode == AccessShareLock)
293  aclmask = ACL_SELECT;
294  else if (lockmode == RowExclusiveLock)
296  else
297  aclmask = ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;
298 
299  aclresult = pg_class_aclcheck(reloid, userid, aclmask);
300 
301  return aclresult;
302 }
#define NIL
Definition: pg_list.h:69
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:138
bool query_tree_walker(Query *query, bool(*walker)(), void *context, int flags)
Definition: nodeFuncs.c:2256
bool nowait
Definition: parsenodes.h:3252
#define IsA(nodeptr, _type_)
Definition: nodes.h:568
static bool LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
Definition: lockcmds.c:188
int LOCKMODE
Definition: lockdefs.h:26
Oid GetUserId(void)
Definition: miscinit.c:379
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:182
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1805
AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId, AclMode mask, AclMaskHow how)
Definition: acl.c:1321
#define AccessShareLock
Definition: lockdefs.h:36
Definition: nodes.h:517
int errcode(int sqlerrcode)
Definition: elog.c:575
#define ACL_DELETE
Definition: parsenodes.h:77
static void LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views)
Definition: lockcmds.c:259
List * lcons_oid(Oid datum, List *list)
Definition: list.c:295
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:84
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:605
char relkind
Definition: pg_class.h:51
char * relname
Definition: primnodes.h:69
uint32 AclMode
Definition: parsenodes.h:72
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3349
static AclResult LockTableAclCheck(Oid relid, LOCKMODE lockmode, Oid userid)
Definition: lockcmds.c:286
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:182
List * rtable
Definition: parsenodes.h:137
#define ObjectIdGetDatum(X)
Definition: postgres.h:490
#define ERROR
Definition: elog.h:43
void PreventCommandDuringRecovery(const char *cmdname)
Definition: utility.c:275
static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid userid)
Definition: lockcmds.c:117
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:227
List * list_delete_oid(List *list, Oid datum)
Definition: list.c:636
#define ereport(elevel, rest)
Definition: elog.h:122
bool inh
Definition: primnodes.h:70
#define ACL_UPDATE
Definition: parsenodes.h:76
AclResult
Definition: acl.h:178
#define ACL_SELECT
Definition: parsenodes.h:75
List * relations
Definition: parsenodes.h:3250
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1294
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition: pg_inherits.c:56
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:505
#define lfirst(lc)
Definition: pg_list.h:106
char * aliasname
Definition: primnodes.h:43
#define ACL_INSERT
Definition: parsenodes.h:74
bool expression_tree_walker(Node *node, bool(*walker)(), void *context)
Definition: nodeFuncs.c:1834
static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: lockcmds.c:81
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4627
void LockTableCommand(LockStmt *lockstmt)
Definition: lockcmds.c:40
int errmsg(const char *fmt,...)
Definition: elog.c:797
Query * get_view_query(Relation view)
ObjectType get_relkind_objtype(char relkind)
void * arg
Alias * eref
Definition: parsenodes.h:1066
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:105
Definition: pg_list.h:45
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1730
#define lfirst_oid(lc)
Definition: pg_list.h:108
#define QTW_IGNORE_JOINALIASES
Definition: nodeFuncs.h:23
#define ACL_TRUNCATE
Definition: parsenodes.h:78