Header And Logo

PostgreSQL
| The world's most advanced open source database.

lockcmds.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * lockcmds.c
00004  *    LOCK command support code
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/commands/lockcmds.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include "access/heapam.h"
00018 #include "catalog/namespace.h"
00019 #include "catalog/pg_inherits_fn.h"
00020 #include "commands/lockcmds.h"
00021 #include "miscadmin.h"
00022 #include "parser/parse_clause.h"
00023 #include "storage/lmgr.h"
00024 #include "utils/acl.h"
00025 #include "utils/lsyscache.h"
00026 #include "utils/syscache.h"
00027 
00028 static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait);
00029 static AclResult LockTableAclCheck(Oid relid, LOCKMODE lockmode);
00030 static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid,
00031                              Oid oldrelid, void *arg);
00032 
00033 /*
00034  * LOCK TABLE
00035  */
00036 void
00037 LockTableCommand(LockStmt *lockstmt)
00038 {
00039     ListCell   *p;
00040 
00041     /*---------
00042      * During recovery we only accept these variations:
00043      * LOCK TABLE foo IN ACCESS SHARE MODE
00044      * LOCK TABLE foo IN ROW SHARE MODE
00045      * LOCK TABLE foo IN ROW EXCLUSIVE MODE
00046      * This test must match the restrictions defined in LockAcquireExtended()
00047      *---------
00048      */
00049     if (lockstmt->mode > RowExclusiveLock)
00050         PreventCommandDuringRecovery("LOCK TABLE");
00051 
00052     /*
00053      * Iterate over the list and process the named relations one at a time
00054      */
00055     foreach(p, lockstmt->relations)
00056     {
00057         RangeVar   *rv = (RangeVar *) lfirst(p);
00058         bool        recurse = interpretInhOption(rv->inhOpt);
00059         Oid         reloid;
00060 
00061         reloid = RangeVarGetRelidExtended(rv, lockstmt->mode, false,
00062                                           lockstmt->nowait,
00063                                           RangeVarCallbackForLockTable,
00064                                           (void *) &lockstmt->mode);
00065 
00066         if (recurse)
00067             LockTableRecurse(reloid, lockstmt->mode, lockstmt->nowait);
00068     }
00069 }
00070 
00071 /*
00072  * Before acquiring a table lock on the named table, check whether we have
00073  * permission to do so.
00074  */
00075 static void
00076 RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
00077                              void *arg)
00078 {
00079     LOCKMODE    lockmode = *(LOCKMODE *) arg;
00080     char        relkind;
00081     AclResult   aclresult;
00082 
00083     if (!OidIsValid(relid))
00084         return;                 /* doesn't exist, so no permissions check */
00085     relkind = get_rel_relkind(relid);
00086     if (!relkind)
00087         return;                 /* woops, concurrently dropped; no permissions
00088                                  * check */
00089 
00090     /* Currently, we only allow plain tables to be locked */
00091     if (relkind != RELKIND_RELATION)
00092         ereport(ERROR,
00093                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00094                  errmsg("\"%s\" is not a table",
00095                         rv->relname)));
00096 
00097     /* Check permissions. */
00098     aclresult = LockTableAclCheck(relid, lockmode);
00099     if (aclresult != ACLCHECK_OK)
00100         aclcheck_error(aclresult, ACL_KIND_CLASS, rv->relname);
00101 }
00102 
00103 /*
00104  * Apply LOCK TABLE recursively over an inheritance tree
00105  *
00106  * We use find_inheritance_children not find_all_inheritors to avoid taking
00107  * locks far in advance of checking privileges.  This means we'll visit
00108  * multiply-inheriting children more than once, but that's no problem.
00109  */
00110 static void
00111 LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
00112 {
00113     List       *children;
00114     ListCell   *lc;
00115 
00116     children = find_inheritance_children(reloid, NoLock);
00117 
00118     foreach(lc, children)
00119     {
00120         Oid         childreloid = lfirst_oid(lc);
00121         AclResult   aclresult;
00122 
00123         /* Check permissions before acquiring the lock. */
00124         aclresult = LockTableAclCheck(childreloid, lockmode);
00125         if (aclresult != ACLCHECK_OK)
00126         {
00127             char       *relname = get_rel_name(childreloid);
00128 
00129             if (!relname)
00130                 continue;       /* child concurrently dropped, just skip it */
00131             aclcheck_error(aclresult, ACL_KIND_CLASS, relname);
00132         }
00133 
00134         /* We have enough rights to lock the relation; do so. */
00135         if (!nowait)
00136             LockRelationOid(childreloid, lockmode);
00137         else if (!ConditionalLockRelationOid(childreloid, lockmode))
00138         {
00139             /* try to throw error by name; relation could be deleted... */
00140             char       *relname = get_rel_name(childreloid);
00141 
00142             if (!relname)
00143                 continue;       /* child concurrently dropped, just skip it */
00144             ereport(ERROR,
00145                     (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
00146                      errmsg("could not obtain lock on relation \"%s\"",
00147                             relname)));
00148         }
00149 
00150         /*
00151          * Even if we got the lock, child might have been concurrently
00152          * dropped. If so, we can skip it.
00153          */
00154         if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(childreloid)))
00155         {
00156             /* Release useless lock */
00157             UnlockRelationOid(childreloid, lockmode);
00158             continue;
00159         }
00160 
00161         LockTableRecurse(childreloid, lockmode, nowait);
00162     }
00163 }
00164 
00165 /*
00166  * Check whether the current user is permitted to lock this relation.
00167  */
00168 static AclResult
00169 LockTableAclCheck(Oid reloid, LOCKMODE lockmode)
00170 {
00171     AclResult   aclresult;
00172 
00173     /* Verify adequate privilege */
00174     if (lockmode == AccessShareLock)
00175         aclresult = pg_class_aclcheck(reloid, GetUserId(),
00176                                       ACL_SELECT);
00177     else
00178         aclresult = pg_class_aclcheck(reloid, GetUserId(),
00179                                       ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE);
00180     return aclresult;
00181 }