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