PostgreSQL Source Code  git master
lockcmds.c File Reference
#include "postgres.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_inherits.h"
#include "commands/lockcmds.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_clause.h"
#include "rewrite/rewriteHandler.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
Include dependency graph for lockcmds.c:

Go to the source code of this file.

Data Structures

struct  LockViewRecurse_context
 

Functions

static void LockTableRecurse (Oid reloid, LOCKMODE lockmode, bool nowait)
 
static AclResult LockTableAclCheck (Oid reloid, LOCKMODE lockmode, Oid userid)
 
static void RangeVarCallbackForLockTable (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
static void LockViewRecurse (Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views)
 
void LockTableCommand (LockStmt *lockstmt)
 
static bool LockViewRecurse_walker (Node *node, LockViewRecurse_context *context)
 

Function Documentation

◆ LockTableAclCheck()

static AclResult LockTableAclCheck ( Oid  reloid,
LOCKMODE  lockmode,
Oid  userid 
)
static

Definition at line 281 of file lockcmds.c.

282 {
283  AclResult aclresult;
285 
286  /* any of these privileges permit any lock mode */
288 
289  /* SELECT privileges also permit ACCESS SHARE and below */
290  if (lockmode <= AccessShareLock)
291  aclmask |= ACL_SELECT;
292 
293  /* INSERT privileges also permit ROW EXCLUSIVE and below */
294  if (lockmode <= RowExclusiveLock)
295  aclmask |= ACL_INSERT;
296 
297  aclresult = pg_class_aclcheck(reloid, userid, aclmask);
298 
299  return aclresult;
300 }
AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId, AclMode mask, AclMaskHow how)
Definition: acl.c:1356
AclResult
Definition: acl.h:181
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:3908
#define AccessShareLock
Definition: lockdefs.h:36
#define RowExclusiveLock
Definition: lockdefs.h:38
#define ACL_DELETE
Definition: parsenodes.h:86
uint64 AclMode
Definition: parsenodes.h:81
#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

References AccessShareLock, ACL_DELETE, ACL_INSERT, ACL_SELECT, ACL_TRUNCATE, ACL_UPDATE, aclmask(), pg_class_aclcheck(), and RowExclusiveLock.

Referenced by LockViewRecurse_walker(), and RangeVarCallbackForLockTable().

◆ LockTableCommand()

void LockTableCommand ( LockStmt lockstmt)

Definition at line 42 of file lockcmds.c.

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 }
static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: lockcmds.c:72
static void LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views)
Definition: lockcmds.c:246
static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
Definition: lockcmds.c:118
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2007
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:221
@ RVR_NOWAIT
Definition: namespace.h:72
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
unsigned int Oid
Definition: postgres_ext.h:31
bool nowait
Definition: parsenodes.h:3821
List * relations
Definition: parsenodes.h:3819
bool inh
Definition: primnodes.h:77

References get_rel_relkind(), RangeVar::inh, lfirst, LockTableRecurse(), LockViewRecurse(), LockStmt::mode, NIL, LockStmt::nowait, RangeVarCallbackForLockTable(), RangeVarGetRelidExtended(), LockStmt::relations, and RVR_NOWAIT.

Referenced by standard_ProcessUtility().

◆ LockTableRecurse()

static void LockTableRecurse ( Oid  reloid,
LOCKMODE  lockmode,
bool  nowait 
)
static

Definition at line 118 of file lockcmds.c.

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 }
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
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
#define NoLock
Definition: lockdefs.h:34
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1932
NameData relname
Definition: pg_class.h:38
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:256
#define lfirst_oid(lc)
Definition: pg_list.h:174
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
Definition: pg_list.h:54
@ RELOID
Definition: syscache.h:89
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:191

References ConditionalLockRelationOid(), ereport, errcode(), errmsg(), ERROR, find_all_inheritors(), get_rel_name(), lfirst_oid, LockRelationOid(), NoLock, ObjectIdGetDatum(), relname, RELOID, SearchSysCacheExists1, and UnlockRelationOid().

Referenced by LockTableCommand(), and LockViewRecurse_walker().

◆ LockViewRecurse()

static void LockViewRecurse ( Oid  reloid,
LOCKMODE  lockmode,
bool  nowait,
List ancestor_views 
)
static

Definition at line 246 of file lockcmds.c.

248 {
249  LockViewRecurse_context context;
250  Relation view;
251  Query *viewquery;
252 
253  /* caller has already locked the view */
254  view = table_open(reloid, NoLock);
255  viewquery = get_view_query(view);
256 
257  /*
258  * If the view has the security_invoker property set, check permissions as
259  * the current user. Otherwise, check permissions as the view owner.
260  */
261  context.lockmode = lockmode;
262  context.nowait = nowait;
263  if (RelationHasSecurityInvoker(view))
264  context.check_as_user = GetUserId();
265  else
266  context.check_as_user = view->rd_rel->relowner;
267  context.viewoid = reloid;
268  context.ancestor_views = lappend_oid(ancestor_views, reloid);
269 
270  LockViewRecurse_walker((Node *) viewquery, &context);
271 
273 
274  table_close(view, NoLock);
275 }
List * lappend_oid(List *list, Oid datum)
Definition: list.c:374
List * list_delete_last(List *list)
Definition: list.c:956
static bool LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
Definition: lockcmds.c:178
Oid GetUserId(void)
Definition: miscinit.c:509
#define RelationHasSecurityInvoker(relation)
Definition: rel.h:435
Query * get_view_query(Relation view)
Definition: nodes.h:129
Form_pg_class rd_rel
Definition: rel.h:111
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40

References LockViewRecurse_context::ancestor_views, LockViewRecurse_context::check_as_user, get_view_query(), GetUserId(), lappend_oid(), list_delete_last(), LockViewRecurse_context::lockmode, LockViewRecurse_walker(), NoLock, LockViewRecurse_context::nowait, RelationData::rd_rel, RelationHasSecurityInvoker, table_close(), table_open(), and LockViewRecurse_context::viewoid.

Referenced by LockTableCommand(), and LockViewRecurse_walker().

◆ LockViewRecurse_walker()

static bool LockViewRecurse_walker ( Node node,
LockViewRecurse_context context 
)
static

Definition at line 178 of file lockcmds.c.

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  /* Currently, we only allow plain tables or views to be locked. */
198  if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE &&
199  relkind != RELKIND_VIEW)
200  continue;
201 
202  /*
203  * We might be dealing with a self-referential view. If so, we
204  * can just stop recursing, since we already locked it.
205  */
206  if (list_member_oid(context->ancestor_views, relid))
207  continue;
208 
209  /*
210  * Check permissions as the specified user. This will either be
211  * the view owner or the current user.
212  */
213  aclresult = LockTableAclCheck(relid, context->lockmode,
214  context->check_as_user);
215  if (aclresult != ACLCHECK_OK)
216  aclcheck_error(aclresult, get_relkind_objtype(relkind), relname);
217 
218  /* We have enough rights to lock the relation; do so. */
219  if (!context->nowait)
220  LockRelationOid(relid, context->lockmode);
221  else if (!ConditionalLockRelationOid(relid, context->lockmode))
222  ereport(ERROR,
223  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
224  errmsg("could not obtain lock on relation \"%s\"",
225  relname)));
226 
227  if (relkind == RELKIND_VIEW)
228  LockViewRecurse(relid, context->lockmode, context->nowait,
229  context->ancestor_views);
230  else if (rte->inh)
231  LockTableRecurse(relid, context->lockmode, context->nowait);
232  }
233 
234  return query_tree_walker(query,
236  context,
238  }
239 
240  return expression_tree_walker(node,
242  context);
243 }
@ ACLCHECK_OK
Definition: acl.h:182
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2669
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:721
static AclResult LockTableAclCheck(Oid reloid, LOCKMODE lockmode, Oid userid)
Definition: lockcmds.c:281
#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)
List * rtable
Definition: parsenodes.h:174

References aclcheck_error(), ACLCHECK_OK, LockViewRecurse_context::ancestor_views, LockViewRecurse_context::check_as_user, ConditionalLockRelationOid(), ereport, errcode(), errmsg(), ERROR, expression_tree_walker, get_rel_name(), get_relkind_objtype(), RangeTblEntry::inh, IsA, lfirst, list_member_oid(), LockViewRecurse_context::lockmode, LockRelationOid(), LockTableAclCheck(), LockTableRecurse(), LockViewRecurse(), LockViewRecurse_context::nowait, QTW_IGNORE_JOINALIASES, query_tree_walker, RangeTblEntry::relid, RangeTblEntry::relkind, relname, and Query::rtable.

Referenced by LockViewRecurse().

◆ RangeVarCallbackForLockTable()

static void RangeVarCallbackForLockTable ( const RangeVar rv,
Oid  relid,
Oid  oldrelid,
void *  arg 
)
static

Definition at line 72 of file lockcmds.c.

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 }
#define OidIsValid(objectId)
Definition: c.h:764
int LOCKMODE
Definition: lockdefs.h:26
char get_rel_persistence(Oid relid)
Definition: lsyscache.c:2082
void * arg
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
char * relname
Definition: primnodes.h:74
int MyXactFlags
Definition: xact.c:136
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:102

References aclcheck_error(), ACLCHECK_OK, arg, ereport, errcode(), errdetail_relkind_not_supported(), errmsg(), ERROR, get_rel_persistence(), get_rel_relkind(), get_relkind_objtype(), GetUserId(), LockTableAclCheck(), MyXactFlags, OidIsValid, RangeVar::relname, and XACT_FLAGS_ACCESSEDTEMPNAMESPACE.

Referenced by LockTableCommand().