LCOV - code coverage report
Current view: top level - src/backend/commands - lockcmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 44 54 81.5 %
Date: 2017-09-29 15:12:54 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * lockcmds.c
       4             :  *    LOCK command support code
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, 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 "catalog/namespace.h"
      18             : #include "catalog/pg_inherits_fn.h"
      19             : #include "commands/lockcmds.h"
      20             : #include "miscadmin.h"
      21             : #include "parser/parse_clause.h"
      22             : #include "storage/lmgr.h"
      23             : #include "utils/acl.h"
      24             : #include "utils/lsyscache.h"
      25             : #include "utils/syscache.h"
      26             : 
      27             : static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait);
      28             : static AclResult LockTableAclCheck(Oid relid, LOCKMODE lockmode);
      29             : static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid,
      30             :                              Oid oldrelid, void *arg);
      31             : 
      32             : /*
      33             :  * LOCK TABLE
      34             :  */
      35             : void
      36          40 : LockTableCommand(LockStmt *lockstmt)
      37             : {
      38             :     ListCell   *p;
      39             : 
      40             :     /*---------
      41             :      * During recovery we only accept these variations:
      42             :      * LOCK TABLE foo IN ACCESS SHARE MODE
      43             :      * LOCK TABLE foo IN ROW SHARE MODE
      44             :      * LOCK TABLE foo IN ROW EXCLUSIVE MODE
      45             :      * This test must match the restrictions defined in LockAcquireExtended()
      46             :      *---------
      47             :      */
      48          40 :     if (lockstmt->mode > RowExclusiveLock)
      49          22 :         PreventCommandDuringRecovery("LOCK TABLE");
      50             : 
      51             :     /*
      52             :      * Iterate over the list and process the named relations one at a time
      53             :      */
      54          68 :     foreach(p, lockstmt->relations)
      55             :     {
      56          40 :         RangeVar   *rv = (RangeVar *) lfirst(p);
      57          40 :         bool        recurse = rv->inh;
      58             :         Oid         reloid;
      59             : 
      60          40 :         reloid = RangeVarGetRelidExtended(rv, lockstmt->mode, false,
      61          40 :                                           lockstmt->nowait,
      62             :                                           RangeVarCallbackForLockTable,
      63          40 :                                           (void *) &lockstmt->mode);
      64             : 
      65          29 :         if (recurse)
      66          28 :             LockTableRecurse(reloid, lockstmt->mode, lockstmt->nowait);
      67             :     }
      68          28 : }
      69             : 
      70             : /*
      71             :  * Before acquiring a table lock on the named table, check whether we have
      72             :  * permission to do so.
      73             :  */
      74             : static void
      75          43 : RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
      76             :                              void *arg)
      77             : {
      78          43 :     LOCKMODE    lockmode = *(LOCKMODE *) arg;
      79             :     char        relkind;
      80             :     AclResult   aclresult;
      81             : 
      82          43 :     if (!OidIsValid(relid))
      83           0 :         return;                 /* doesn't exist, so no permissions check */
      84          43 :     relkind = get_rel_relkind(relid);
      85          43 :     if (!relkind)
      86           0 :         return;                 /* woops, concurrently dropped; no permissions
      87             :                                  * check */
      88             : 
      89             :     /* Currently, we only allow plain tables to be locked */
      90          43 :     if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
      91           1 :         ereport(ERROR,
      92             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
      93             :                  errmsg("\"%s\" is not a table",
      94             :                         rv->relname)));
      95             : 
      96             :     /* Check permissions. */
      97          42 :     aclresult = LockTableAclCheck(relid, lockmode);
      98          42 :     if (aclresult != ACLCHECK_OK)
      99           8 :         aclcheck_error(aclresult, ACL_KIND_CLASS, rv->relname);
     100             : }
     101             : 
     102             : /*
     103             :  * Apply LOCK TABLE recursively over an inheritance tree
     104             :  *
     105             :  * We use find_inheritance_children not find_all_inheritors to avoid taking
     106             :  * locks far in advance of checking privileges.  This means we'll visit
     107             :  * multiply-inheriting children more than once, but that's no problem.
     108             :  */
     109             : static void
     110          30 : LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
     111             : {
     112             :     List       *children;
     113             :     ListCell   *lc;
     114             : 
     115          30 :     children = find_inheritance_children(reloid, NoLock);
     116             : 
     117          32 :     foreach(lc, children)
     118             :     {
     119           3 :         Oid         childreloid = lfirst_oid(lc);
     120             :         AclResult   aclresult;
     121             : 
     122             :         /* Check permissions before acquiring the lock. */
     123           3 :         aclresult = LockTableAclCheck(childreloid, lockmode);
     124           3 :         if (aclresult != ACLCHECK_OK)
     125             :         {
     126           1 :             char       *relname = get_rel_name(childreloid);
     127             : 
     128           1 :             if (!relname)
     129           0 :                 continue;       /* child concurrently dropped, just skip it */
     130           1 :             aclcheck_error(aclresult, ACL_KIND_CLASS, relname);
     131             :         }
     132             : 
     133             :         /* We have enough rights to lock the relation; do so. */
     134           2 :         if (!nowait)
     135           2 :             LockRelationOid(childreloid, lockmode);
     136           0 :         else if (!ConditionalLockRelationOid(childreloid, lockmode))
     137             :         {
     138             :             /* try to throw error by name; relation could be deleted... */
     139           0 :             char       *relname = get_rel_name(childreloid);
     140             : 
     141           0 :             if (!relname)
     142           0 :                 continue;       /* child concurrently dropped, just skip it */
     143           0 :             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           2 :         if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(childreloid)))
     154             :         {
     155             :             /* Release useless lock */
     156           0 :             UnlockRelationOid(childreloid, lockmode);
     157           0 :             continue;
     158             :         }
     159             : 
     160           2 :         LockTableRecurse(childreloid, lockmode, nowait);
     161             :     }
     162          29 : }
     163             : 
     164             : /*
     165             :  * Check whether the current user is permitted to lock this relation.
     166             :  */
     167             : static AclResult
     168          45 : LockTableAclCheck(Oid reloid, LOCKMODE lockmode)
     169             : {
     170             :     AclResult   aclresult;
     171             :     AclMode     aclmask;
     172             : 
     173             :     /* Verify adequate privilege */
     174          45 :     if (lockmode == AccessShareLock)
     175           9 :         aclmask = ACL_SELECT;
     176          36 :     else if (lockmode == RowExclusiveLock)
     177           7 :         aclmask = ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;
     178             :     else
     179          29 :         aclmask = ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;
     180             : 
     181          45 :     aclresult = pg_class_aclcheck(reloid, GetUserId(), aclmask);
     182             : 
     183          45 :     return aclresult;
     184             : }

Generated by: LCOV version 1.11