LCOV - code coverage report
Current view: top level - src/backend/commands - lockcmds.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 89.1 % 119 106
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 66.0 % 100 66

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * lockcmds.c
       4                 :             :  *        LOCK command support code
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, 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 "rewrite/rewriteHandler.h"
      25                 :             : #include "storage/lmgr.h"
      26                 :             : #include "utils/acl.h"
      27                 :             : #include "utils/lsyscache.h"
      28                 :             : #include "utils/syscache.h"
      29                 :             : 
      30                 :             : static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait);
      31                 :             : static AclResult LockTableAclCheck(Oid reloid, LOCKMODE lockmode, Oid userid);
      32                 :             : static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid,
      33                 :             :                                                                                  Oid oldrelid, void *arg);
      34                 :             : static void LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait,
      35                 :             :                                                         List *ancestor_views);
      36                 :             : 
      37                 :             : /*
      38                 :             :  * LOCK TABLE
      39                 :             :  */
      40                 :             : void
      41                 :          68 : LockTableCommand(LockStmt *lockstmt)
      42                 :             : {
      43                 :          68 :         ListCell   *p;
      44                 :             : 
      45                 :             :         /*
      46                 :             :          * Iterate over the list and process the named relations one at a time
      47                 :             :          */
      48   [ +  +  +  +  :         121 :         foreach(p, lockstmt->relations)
                   +  + ]
      49                 :             :         {
      50                 :          53 :                 RangeVar   *rv = (RangeVar *) lfirst(p);
      51                 :          53 :                 bool            recurse = rv->inh;
      52                 :          53 :                 Oid                     reloid;
      53                 :             : 
      54                 :         106 :                 reloid = RangeVarGetRelidExtended(rv, lockstmt->mode,
      55                 :          53 :                                                                                   lockstmt->nowait ? RVR_NOWAIT : 0,
      56                 :             :                                                                                   RangeVarCallbackForLockTable,
      57                 :          53 :                                                                                   &lockstmt->mode);
      58                 :             : 
      59         [ +  + ]:          53 :                 if (get_rel_relkind(reloid) == RELKIND_VIEW)
      60                 :          11 :                         LockViewRecurse(reloid, lockstmt->mode, lockstmt->nowait, NIL);
      61         [ +  + ]:          42 :                 else if (recurse)
      62                 :          41 :                         LockTableRecurse(reloid, lockstmt->mode, lockstmt->nowait);
      63                 :          53 :         }
      64                 :          52 : }
      65                 :             : 
      66                 :             : /*
      67                 :             :  * Before acquiring a table lock on the named table, check whether we have
      68                 :             :  * permission to do so.
      69                 :             :  */
      70                 :             : static void
      71                 :          82 : RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
      72                 :             :                                                          void *arg)
      73                 :             : {
      74                 :          82 :         LOCKMODE        lockmode = *(LOCKMODE *) arg;
      75                 :          82 :         char            relkind;
      76                 :          82 :         char            relpersistence;
      77                 :          82 :         AclResult       aclresult;
      78                 :             : 
      79         [ +  - ]:          82 :         if (!OidIsValid(relid))
      80                 :           0 :                 return;                                 /* doesn't exist, so no permissions check */
      81                 :          82 :         relkind = get_rel_relkind(relid);
      82         [ +  - ]:          82 :         if (!relkind)
      83                 :           0 :                 return;                                 /* woops, concurrently dropped; no permissions
      84                 :             :                                                                  * check */
      85                 :             : 
      86                 :             :         /* Currently, we only allow plain tables or views to be locked */
      87   [ +  +  +  -  :          82 :         if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE &&
                   +  - ]
      88                 :          21 :                 relkind != RELKIND_VIEW)
      89   [ #  #  #  # ]:           0 :                 ereport(ERROR,
      90                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
      91                 :             :                                  errmsg("cannot lock relation \"%s\"",
      92                 :             :                                                 rv->relname),
      93                 :             :                                  errdetail_relkind_not_supported(relkind)));
      94                 :             : 
      95                 :             :         /*
      96                 :             :          * Make note if a temporary relation has been accessed in this
      97                 :             :          * transaction.
      98                 :             :          */
      99                 :          82 :         relpersistence = get_rel_persistence(relid);
     100         [ +  + ]:          82 :         if (relpersistence == RELPERSISTENCE_TEMP)
     101                 :           2 :                 MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
     102                 :             : 
     103                 :             :         /* Check permissions. */
     104                 :          82 :         aclresult = LockTableAclCheck(relid, lockmode, GetUserId());
     105         [ +  + ]:          82 :         if (aclresult != ACLCHECK_OK)
     106                 :           8 :                 aclcheck_error(aclresult, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
     107         [ -  + ]:          82 : }
     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                 :             :  */
     116                 :             : static void
     117                 :          53 : LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
     118                 :             : {
     119                 :          53 :         List       *children;
     120                 :          53 :         ListCell   *lc;
     121                 :             : 
     122                 :          53 :         children = find_all_inheritors(reloid, NoLock, NULL);
     123                 :             : 
     124   [ +  -  +  +  :         115 :         foreach(lc, children)
                   +  + ]
     125                 :             :         {
     126                 :          62 :                 Oid                     childreloid = lfirst_oid(lc);
     127                 :             : 
     128                 :             :                 /* Parent already locked. */
     129         [ +  + ]:          62 :                 if (childreloid == reloid)
     130                 :          53 :                         continue;
     131                 :             : 
     132         [ -  + ]:           9 :                 if (!nowait)
     133                 :           9 :                         LockRelationOid(childreloid, lockmode);
     134         [ #  # ]:           0 :                 else if (!ConditionalLockRelationOid(childreloid, lockmode))
     135                 :             :                 {
     136                 :             :                         /* try to throw error by name; relation could be deleted... */
     137                 :           0 :                         char       *relname = get_rel_name(childreloid);
     138                 :             : 
     139         [ #  # ]:           0 :                         if (!relname)
     140                 :           0 :                                 continue;               /* child concurrently dropped, just skip it */
     141   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     142                 :             :                                         (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
     143                 :             :                                          errmsg("could not obtain lock on relation \"%s\"",
     144                 :             :                                                         relname)));
     145         [ #  # ]:           0 :                 }
     146                 :             : 
     147                 :             :                 /*
     148                 :             :                  * Even if we got the lock, child might have been concurrently
     149                 :             :                  * dropped. If so, we can skip it.
     150                 :             :                  */
     151         [ +  - ]:           9 :                 if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(childreloid)))
     152                 :             :                 {
     153                 :             :                         /* Release useless lock */
     154                 :           0 :                         UnlockRelationOid(childreloid, lockmode);
     155                 :           0 :                         continue;
     156                 :             :                 }
     157      [ -  +  + ]:          62 :         }
     158                 :          53 : }
     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                 :             : 
     167                 :             : typedef 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 */
     174                 :             : } LockViewRecurse_context;
     175                 :             : 
     176                 :             : static bool
     177                 :         351 : LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
     178                 :             : {
     179         [ +  + ]:         351 :         if (node == NULL)
     180                 :         223 :                 return false;
     181                 :             : 
     182         [ +  + ]:         128 :         if (IsA(node, Query))
     183                 :             :         {
     184                 :          19 :                 Query      *query = (Query *) node;
     185                 :          19 :                 ListCell   *rtable;
     186                 :             : 
     187   [ +  +  +  +  :          38 :                 foreach(rtable, query->rtable)
                   +  + ]
     188                 :             :                 {
     189                 :          19 :                         RangeTblEntry *rte = lfirst(rtable);
     190                 :          19 :                         AclResult       aclresult;
     191                 :             : 
     192                 :          19 :                         Oid                     relid = rte->relid;
     193                 :          19 :                         char            relkind = rte->relkind;
     194                 :          19 :                         char       *relname = get_rel_name(relid);
     195                 :             : 
     196                 :             :                         /* Currently, we only allow plain tables or views to be locked. */
     197   [ +  +  +  -  :          19 :                         if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE &&
                   +  + ]
     198                 :           7 :                                 relkind != RELKIND_VIEW)
     199                 :           1 :                                 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         [ +  + ]:          18 :                         if (list_member_oid(context->ancestor_views, relid))
     206                 :           2 :                                 continue;
     207                 :             : 
     208                 :             :                         /*
     209                 :             :                          * Check permissions as the specified user.  This will either be
     210                 :             :                          * the view owner or the current user.
     211                 :             :                          */
     212                 :          32 :                         aclresult = LockTableAclCheck(relid, context->lockmode,
     213                 :          16 :                                                                                   context->check_as_user);
     214         [ +  + ]:          16 :                         if (aclresult != ACLCHECK_OK)
     215                 :           1 :                                 aclcheck_error(aclresult, get_relkind_objtype(relkind), relname);
     216                 :             : 
     217                 :             :                         /* We have enough rights to lock the relation; do so. */
     218         [ -  + ]:          16 :                         if (!context->nowait)
     219                 :          16 :                                 LockRelationOid(relid, context->lockmode);
     220         [ #  # ]:           0 :                         else if (!ConditionalLockRelationOid(relid, context->lockmode))
     221   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
     222                 :             :                                                 (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
     223                 :             :                                                  errmsg("could not obtain lock on relation \"%s\"",
     224                 :             :                                                                 relname)));
     225                 :             : 
     226         [ +  + ]:          16 :                         if (relkind == RELKIND_VIEW)
     227                 :           8 :                                 LockViewRecurse(relid, context->lockmode, context->nowait,
     228                 :           4 :                                                                 context->ancestor_views);
     229         [ -  + ]:          12 :                         else if (rte->inh)
     230                 :          12 :                                 LockTableRecurse(relid, context->lockmode, context->nowait);
     231      [ -  +  + ]:          19 :                 }
     232                 :             : 
     233                 :          17 :                 return query_tree_walker(query,
     234                 :             :                                                                  LockViewRecurse_walker,
     235                 :             :                                                                  context,
     236                 :             :                                                                  QTW_IGNORE_JOINALIASES);
     237                 :          17 :         }
     238                 :             : 
     239                 :         109 :         return expression_tree_walker(node,
     240                 :             :                                                                   LockViewRecurse_walker,
     241                 :             :                                                                   context);
     242                 :         349 : }
     243                 :             : 
     244                 :             : static void
     245                 :          15 : LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait,
     246                 :             :                                 List *ancestor_views)
     247                 :             : {
     248                 :          15 :         LockViewRecurse_context context;
     249                 :          15 :         Relation        view;
     250                 :          15 :         Query      *viewquery;
     251                 :             : 
     252                 :             :         /* caller has already locked the view */
     253                 :          15 :         view = table_open(reloid, NoLock);
     254                 :          15 :         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                 :          15 :         context.lockmode = lockmode;
     261                 :          15 :         context.nowait = nowait;
     262   [ +  -  +  +  :          15 :         if (RelationHasSecurityInvoker(view))
                   +  + ]
     263                 :           2 :                 context.check_as_user = GetUserId();
     264                 :             :         else
     265                 :          13 :                 context.check_as_user = view->rd_rel->relowner;
     266                 :          15 :         context.viewoid = reloid;
     267                 :          15 :         context.ancestor_views = lappend_oid(ancestor_views, reloid);
     268                 :             : 
     269                 :          15 :         LockViewRecurse_walker((Node *) viewquery, &context);
     270                 :             : 
     271                 :          15 :         context.ancestor_views = list_delete_last(context.ancestor_views);
     272                 :             : 
     273                 :          15 :         table_close(view, NoLock);
     274                 :          15 : }
     275                 :             : 
     276                 :             : /*
     277                 :             :  * Check whether the current user is permitted to lock this relation.
     278                 :             :  */
     279                 :             : static AclResult
     280                 :         107 : LockTableAclCheck(Oid reloid, LOCKMODE lockmode, Oid userid)
     281                 :             : {
     282                 :         107 :         AclResult       aclresult;
     283                 :         107 :         AclMode         aclmask;
     284                 :             : 
     285                 :             :         /* any of these privileges permit any lock mode */
     286                 :         107 :         aclmask = ACL_MAINTAIN | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;
     287                 :             : 
     288                 :             :         /* SELECT privileges also permit ACCESS SHARE and below */
     289         [ +  + ]:         107 :         if (lockmode <= AccessShareLock)
     290                 :          14 :                 aclmask |= ACL_SELECT;
     291                 :             : 
     292                 :             :         /* INSERT privileges also permit ROW EXCLUSIVE and below */
     293         [ +  + ]:         107 :         if (lockmode <= RowExclusiveLock)
     294                 :          27 :                 aclmask |= ACL_INSERT;
     295                 :             : 
     296                 :         107 :         aclresult = pg_class_aclcheck(reloid, userid, aclmask);
     297                 :             : 
     298                 :         214 :         return aclresult;
     299                 :         107 : }
        

Generated by: LCOV version 2.3.2-1