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-2021, 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 "nodes/nodeFuncs.h"
24 #include "parser/parse_clause.h"
25 #include "rewrite/rewriteHandler.h"
26 #include "storage/lmgr.h"
27 #include "utils/acl.h"
28 #include "utils/lsyscache.h"
29 #include "utils/syscache.h"
30 
31 static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait);
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,
36  List *ancestor_views);
37 
38 /*
39  * LOCK TABLE
40  */
41 void
43 {
44  ListCell *p;
45 
46  /*
47  * Iterate over the list and process the named relations one at a time
48  */
49  foreach(p, lockstmt->relations)
50  {
51  RangeVar *rv = (RangeVar *) lfirst(p);
52  bool recurse = rv->inh;
53  Oid reloid;
54 
55  reloid = RangeVarGetRelidExtended(rv, lockstmt->mode,
56  lockstmt->nowait ? RVR_NOWAIT : 0,
58  (void *) &lockstmt->mode);
59 
60  if (get_rel_relkind(reloid) == RELKIND_VIEW)
61  LockViewRecurse(reloid, lockstmt->mode, lockstmt->nowait, NIL);
62  else if (recurse)
63  LockTableRecurse(reloid, lockstmt->mode, lockstmt->nowait);
64  }
65 }
66 
67 /*
68  * Before acquiring a table lock on the named table, check whether we have
69  * permission to do so.
70  */
71 static void
72 RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
73  void *arg)
74 {
75  LOCKMODE lockmode = *(LOCKMODE *) arg;
76  char relkind;
77  char relpersistence;
78  AclResult aclresult;
79 
80  if (!OidIsValid(relid))
81  return; /* doesn't exist, so no permissions check */
82  relkind = get_rel_relkind(relid);
83  if (!relkind)
84  return; /* woops, concurrently dropped; no permissions
85  * check */
86 
87  /* Currently, we only allow plain tables or views to be locked */
88  if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE &&
89  relkind != RELKIND_VIEW)
90  ereport(ERROR,
91  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
92  errmsg("cannot lock relation \"%s\"",
93  rv->relname),
95 
96  /*
97  * Make note if a temporary relation has been accessed in this
98  * transaction.
99  */
100  relpersistence = get_rel_persistence(relid);
101  if (relpersistence == RELPERSISTENCE_TEMP)
103 
104  /* Check permissions. */
105  aclresult = LockTableAclCheck(relid, lockmode, GetUserId());
106  if (aclresult != ACLCHECK_OK)
108 }
109 
110 /*
111  * Apply LOCK TABLE recursively over an inheritance tree
112  *
113  * This doesn't check permission to perform LOCK TABLE on the child tables,
114  * because getting here means that the user has permission to lock the
115  * parent which is enough.
116  */
117 static void
118 LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
119 {
120  List *children;
121  ListCell *lc;
122 
123  children = find_all_inheritors(reloid, NoLock, NULL);
124 
125  foreach(lc, children)
126  {
127  Oid childreloid = lfirst_oid(lc);
128 
129  /* Parent already locked. */
130  if (childreloid == reloid)
131  continue;
132 
133  if (!nowait)
134  LockRelationOid(childreloid, lockmode);
135  else if (!ConditionalLockRelationOid(childreloid, lockmode))
136  {
137  /* try to throw error by name; relation could be deleted... */
138  char *relname = get_rel_name(childreloid);
139 
140  if (!relname)
141  continue; /* child concurrently dropped, just skip it */
142  ereport(ERROR,
143  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
144  errmsg("could not obtain lock on relation \"%s\"",
145  relname)));
146  }
147 
148  /*
149  * Even if we got the lock, child might have been concurrently
150  * dropped. If so, we can skip it.
151  */
152  if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(childreloid)))
153  {
154  /* Release useless lock */
155  UnlockRelationOid(childreloid, lockmode);
156  continue;
157  }
158  }
159 }
160 
161 /*
162  * Apply LOCK TABLE recursively over a view
163  *
164  * All tables and views appearing in the view definition query are locked
165  * recursively with the same lock mode.
166  */
167 
168 typedef struct
169 {
170  LOCKMODE lockmode; /* lock mode to use */
171  bool nowait; /* no wait mode */
172  Oid viewowner; /* view owner for checking the privilege */
173  Oid viewoid; /* OID of the view to be locked */
174  List *ancestor_views; /* OIDs of ancestor views */
176 
177 static bool
179 {
180  if (node == NULL)
181  return false;
182 
183  if (IsA(node, Query))
184  {
185  Query *query = (Query *) node;
186  ListCell *rtable;
187 
188  foreach(rtable, query->rtable)
189  {
190  RangeTblEntry *rte = lfirst(rtable);
191  AclResult aclresult;
192 
193  Oid relid = rte->relid;
194  char relkind = rte->relkind;
195  char *relname = get_rel_name(relid);
196 
197  /*
198  * The OLD and NEW placeholder entries in the view's rtable are
199  * skipped.
200  */
201  if (relid == context->viewoid &&
202  (strcmp(rte->eref->aliasname, "old") == 0 ||
203  strcmp(rte->eref->aliasname, "new") == 0))
204  continue;
205 
206  /* Currently, we only allow plain tables or views to be locked. */
207  if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE &&
208  relkind != RELKIND_VIEW)
209  continue;
210 
211  /*
212  * We might be dealing with a self-referential view. If so, we
213  * can just stop recursing, since we already locked it.
214  */
215  if (list_member_oid(context->ancestor_views, relid))
216  continue;
217 
218  /* Check permissions with the view owner's privilege. */
219  aclresult = LockTableAclCheck(relid, context->lockmode, context->viewowner);
220  if (aclresult != ACLCHECK_OK)
221  aclcheck_error(aclresult, get_relkind_objtype(relkind), relname);
222 
223  /* We have enough rights to lock the relation; do so. */
224  if (!context->nowait)
225  LockRelationOid(relid, context->lockmode);
226  else if (!ConditionalLockRelationOid(relid, context->lockmode))
227  ereport(ERROR,
228  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
229  errmsg("could not obtain lock on relation \"%s\"",
230  relname)));
231 
232  if (relkind == RELKIND_VIEW)
233  LockViewRecurse(relid, context->lockmode, context->nowait,
234  context->ancestor_views);
235  else if (rte->inh)
236  LockTableRecurse(relid, context->lockmode, context->nowait);
237  }
238 
239  return query_tree_walker(query,
241  context,
243  }
244 
245  return expression_tree_walker(node,
247  context);
248 }
249 
250 static void
251 LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait,
252  List *ancestor_views)
253 {
254  LockViewRecurse_context context;
255  Relation view;
256  Query *viewquery;
257 
258  /* caller has already locked the view */
259  view = table_open(reloid, NoLock);
260  viewquery = get_view_query(view);
261 
262  context.lockmode = lockmode;
263  context.nowait = nowait;
264  context.viewowner = view->rd_rel->relowner;
265  context.viewoid = reloid;
266  context.ancestor_views = lappend_oid(ancestor_views, reloid);
267 
268  LockViewRecurse_walker((Node *) viewquery, &context);
269 
271 
272  table_close(view, NoLock);
273 }
274 
275 /*
276  * Check whether the current user is permitted to lock this relation.
277  */
278 static AclResult
279 LockTableAclCheck(Oid reloid, LOCKMODE lockmode, Oid userid)
280 {
281  AclResult aclresult;
283 
284  /* Verify adequate privilege */
285  if (lockmode == AccessShareLock)
286  aclmask = ACL_SELECT;
287  else if (lockmode == RowExclusiveLock)
289  else
290  aclmask = ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;
291 
292  aclresult = pg_class_aclcheck(reloid, userid, aclmask);
293 
294  return aclresult;
295 }
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
#define NIL
Definition: pg_list.h:65
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:152
bool query_tree_walker(Query *query, bool(*walker)(), void *context, int flags)
Definition: nodeFuncs.c:2355
bool nowait
Definition: parsenodes.h:3452
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
static bool LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
Definition: lockcmds.c:178
int LOCKMODE
Definition: lockdefs.h:26
Oid GetUserId(void)
Definition: miscinit.c:478
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:200
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1974
AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId, AclMode mask, AclMaskHow how)
Definition: acl.c:1306
#define AccessShareLock
Definition: lockdefs.h:36
Definition: nodes.h:539
int errcode(int sqlerrcode)
Definition: elog.c:698
#define ACL_DELETE
Definition: parsenodes.h:85
static void LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views)
Definition: lockcmds.c:251
Form_pg_class rd_rel
Definition: rel.h:109
NameData relname
Definition: pg_class.h:38
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:372
#define OidIsValid(objectId)
Definition: c.h:710
char * relname
Definition: primnodes.h:68
uint32 AclMode
Definition: parsenodes.h:80
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3308
static AclResult LockTableAclCheck(Oid relid, LOCKMODE lockmode, Oid userid)
Definition: lockcmds.c:279
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:184
List * rtable
Definition: parsenodes.h:147
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
List * list_delete_last(List *list)
Definition: list.c:892
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:237
bool inh
Definition: primnodes.h:69
int MyXactFlags
Definition: xact.c:132
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:102
#define ACL_UPDATE
Definition: parsenodes.h:84
static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
Definition: lockcmds.c:118
AclResult
Definition: acl.h:177
#define ACL_SELECT
Definition: parsenodes.h:83
List * relations
Definition: parsenodes.h:3450
#define ereport(elevel,...)
Definition: elog.h:157
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:689
#define lfirst(lc)
Definition: pg_list.h:169
char * aliasname
Definition: primnodes.h:42
#define ACL_INSERT
Definition: parsenodes.h:82
bool expression_tree_walker(Node *node, bool(*walker)(), void *context)
Definition: nodeFuncs.c:1904
static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: lockcmds.c:72
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4680
char get_rel_persistence(Oid relid)
Definition: lsyscache.c:2049
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:256
void LockTableCommand(LockStmt *lockstmt)
Definition: lockcmds.c:42
int errmsg(const char *fmt,...)
Definition: elog.c:909
Query * get_view_query(Relation view)
ObjectType get_relkind_objtype(char relkind)
void * arg
Alias * eref
Definition: parsenodes.h:1141
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:109
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:1899
#define lfirst_oid(lc)
Definition: pg_list.h:171
#define QTW_IGNORE_JOINALIASES
Definition: nodeFuncs.h:23
#define ACL_TRUNCATE
Definition: parsenodes.h:86