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