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