]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/lockfuncs.c
Update copyrights in source tree to 2008.
[postgresql] / src / backend / utils / adt / lockfuncs.c
index 8fb1e6dbaebaea7564a19cf2d25aefbfb29f30e5..03c4d65e895caa862cb4200091f56c19c4375401 100644 (file)
@@ -1,25 +1,38 @@
 /*-------------------------------------------------------------------------
  *
  * lockfuncs.c
- *             Set-returning functions to view the state of locks within the DB.
+ *             Functions for SQL access to various lock-manager capabilities.
  *
- * Copyright (c) 2002-2003, PostgreSQL Global Development Group
+ * Copyright (c) 2002-2008, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *             $Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.11 2003/08/04 23:59:38 tgl Exp $
+ *             $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.31 2008/01/01 19:45:52 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include "funcapi.h"
 #include "access/heapam.h"
 #include "catalog/pg_type.h"
-#include "storage/lock.h"
+#include "funcapi.h"
+#include "miscadmin.h"
 #include "storage/proc.h"
 #include "utils/builtins.h"
 
 
+/* This must match enum LockTagType! */
+static const char *const LockTagTypeNames[] = {
+       "relation",
+       "extend",
+       "page",
+       "tuple",
+       "transactionid",
+       "virtualxid",
+       "object",
+       "userlock",
+       "advisory"
+};
+
 /* Working status for pg_lock_status */
 typedef struct
 {
@@ -27,6 +40,27 @@ typedef struct
        int                     currIdx;                /* current PROCLOCK index */
 } PG_Lock_Status;
 
+
+/*
+ * VXIDGetDatum - Construct a text representation of a VXID
+ *
+ * This is currently only used in pg_lock_status, so we put it here.
+ */
+static Datum
+VXIDGetDatum(BackendId bid, LocalTransactionId lxid)
+{
+       /*
+        * The representation is "<bid>/<lxid>", decimal and unsigned decimal
+        * respectively.  Note that elog.c also knows how to format a vxid.
+        */
+       char            vxidstr[32];
+
+       snprintf(vxidstr, sizeof(vxidstr), "%d/%u", bid, lxid);
+
+       return DirectFunctionCall1(textin, CStringGetDatum(vxidstr));
+}
+
+
 /*
  * pg_lock_status - produce a view with one row per held or awaited lock mode
  */
@@ -46,32 +80,47 @@ pg_lock_status(PG_FUNCTION_ARGS)
                funcctx = SRF_FIRSTCALL_INIT();
 
                /*
-                * switch to memory context appropriate for multiple function
-                * calls
+                * switch to memory context appropriate for multiple function calls
                 */
                oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
                /* build tupdesc for result tuples */
-               /* this had better match pg_locks view in initdb.sh */
-               tupdesc = CreateTemplateTupleDesc(6, false);
-               TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation",
-                                                  OIDOID, -1, 0, false);
+               /* this had better match pg_locks view in system_views.sql */
+               tupdesc = CreateTemplateTupleDesc(14, false);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype",
+                                                  TEXTOID, -1, 0);
                TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
-                                                  OIDOID, -1, 0, false);
-               TupleDescInitEntry(tupdesc, (AttrNumber) 3, "transaction",
-                                                  XIDOID, -1, 0, false);
-               TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pid",
-                                                  INT4OID, -1, 0, false);
-               TupleDescInitEntry(tupdesc, (AttrNumber) 5, "mode",
-                                                  TEXTOID, -1, 0, false);
-               TupleDescInitEntry(tupdesc, (AttrNumber) 6, "granted",
-                                                  BOOLOID, -1, 0, false);
-
-               funcctx->slot = TupleDescGetSlot(tupdesc);
+                                                  OIDOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relation",
+                                                  OIDOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 4, "page",
+                                                  INT4OID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 5, "tuple",
+                                                  INT2OID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 6, "virtualxid",
+                                                  TEXTOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 7, "transactionid",
+                                                  XIDOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 8, "classid",
+                                                  OIDOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objid",
+                                                  OIDOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 10, "objsubid",
+                                                  INT2OID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 11, "virtualtransaction",
+                                                  TEXTOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 12, "pid",
+                                                  INT4OID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 13, "mode",
+                                                  TEXTOID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 14, "granted",
+                                                  BOOLOID, -1, 0);
+
+               funcctx->tuple_desc = BlessTupleDesc(tupdesc);
 
                /*
-                * Collect all the locking information that we will format and
-                * send out as a result set.
+                * Collect all the locking information that we will format and send
+                * out as a result set.
                 */
                mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status));
                funcctx->user_fctx = (void *) mystatus;
@@ -92,9 +141,11 @@ pg_lock_status(PG_FUNCTION_ARGS)
                LOCK       *lock;
                PGPROC     *proc;
                bool            granted;
-               LOCKMODE        mode;
-               Datum           values[6];
-               char            nulls[6];
+               LOCKMODE        mode = 0;
+               const char *locktypename;
+               char            tnbuf[32];
+               Datum           values[14];
+               char            nulls[14];
                HeapTuple       tuple;
                Datum           result;
 
@@ -103,18 +154,21 @@ pg_lock_status(PG_FUNCTION_ARGS)
                proc = &(lockData->procs[mystatus->currIdx]);
 
                /*
-                * Look to see if there are any held lock modes in this PROCLOCK.
-                * If so, report, and destructively modify lockData so we don't
-                * report again.
+                * Look to see if there are any held lock modes in this PROCLOCK. If
+                * so, report, and destructively modify lockData so we don't report
+                * again.
                 */
                granted = false;
-               for (mode = 0; mode < MAX_LOCKMODES; mode++)
+               if (proclock->holdMask)
                {
-                       if (proclock->holding[mode] > 0)
+                       for (mode = 0; mode < MAX_LOCKMODES; mode++)
                        {
-                               granted = true;
-                               proclock->holding[mode] = 0;
-                               break;
+                               if (proclock->holdMask & LOCKBIT_ON(mode))
+                               {
+                                       granted = true;
+                                       proclock->holdMask &= LOCKBIT_OFF(mode);
+                                       break;
+                               }
                        }
                }
 
@@ -124,22 +178,22 @@ pg_lock_status(PG_FUNCTION_ARGS)
                 */
                if (!granted)
                {
-                       if (proc->waitLock == (LOCK *) MAKE_PTR(proclock->tag.lock))
+                       if (proc->waitLock == proclock->tag.myLock)
                        {
                                /* Yes, so report it with proper mode */
                                mode = proc->waitLockMode;
 
                                /*
-                                * We are now done with this PROCLOCK, so advance pointer
-                                * to continue with next one on next call.
+                                * We are now done with this PROCLOCK, so advance pointer to
+                                * continue with next one on next call.
                                 */
                                mystatus->currIdx++;
                        }
                        else
                        {
                                /*
-                                * Okay, we've displayed all the locks associated with
-                                * this PROCLOCK, proceed to the next one.
+                                * Okay, we've displayed all the locks associated with this
+                                * PROCLOCK, proceed to the next one.
                                 */
                                mystatus->currIdx++;
                                continue;
@@ -152,32 +206,359 @@ pg_lock_status(PG_FUNCTION_ARGS)
                MemSet(values, 0, sizeof(values));
                MemSet(nulls, ' ', sizeof(nulls));
 
-               if (lock->tag.relId == XactLockTableId && lock->tag.dbId == 0)
-               {
-                       /* Lock is for transaction ID */
-                       nulls[0] = 'n';
-                       nulls[1] = 'n';
-                       values[2] = TransactionIdGetDatum(lock->tag.objId.xid);
-               }
+               if (lock->tag.locktag_type <= LOCKTAG_ADVISORY)
+                       locktypename = LockTagTypeNames[lock->tag.locktag_type];
                else
                {
-                       /* Lock is for a relation */
-                       values[0] = ObjectIdGetDatum(lock->tag.relId);
-                       values[1] = ObjectIdGetDatum(lock->tag.dbId);
-                       nulls[2] = 'n';
+                       snprintf(tnbuf, sizeof(tnbuf), "unknown %d",
+                                        (int) lock->tag.locktag_type);
+                       locktypename = tnbuf;
+               }
+               values[0] = DirectFunctionCall1(textin,
+                                                                               CStringGetDatum(locktypename));
 
+               switch (lock->tag.locktag_type)
+               {
+                       case LOCKTAG_RELATION:
+                       case LOCKTAG_RELATION_EXTEND:
+                               values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
+                               values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
+                               nulls[3] = 'n';
+                               nulls[4] = 'n';
+                               nulls[5] = 'n';
+                               nulls[6] = 'n';
+                               nulls[7] = 'n';
+                               nulls[8] = 'n';
+                               nulls[9] = 'n';
+                               break;
+                       case LOCKTAG_PAGE:
+                               values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
+                               values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
+                               values[3] = UInt32GetDatum(lock->tag.locktag_field3);
+                               nulls[4] = 'n';
+                               nulls[5] = 'n';
+                               nulls[6] = 'n';
+                               nulls[7] = 'n';
+                               nulls[8] = 'n';
+                               nulls[9] = 'n';
+                               break;
+                       case LOCKTAG_TUPLE:
+                               values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
+                               values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
+                               values[3] = UInt32GetDatum(lock->tag.locktag_field3);
+                               values[4] = UInt16GetDatum(lock->tag.locktag_field4);
+                               nulls[5] = 'n';
+                               nulls[6] = 'n';
+                               nulls[7] = 'n';
+                               nulls[8] = 'n';
+                               nulls[9] = 'n';
+                               break;
+                       case LOCKTAG_TRANSACTION:
+                               values[6] = TransactionIdGetDatum(lock->tag.locktag_field1);
+                               nulls[1] = 'n';
+                               nulls[2] = 'n';
+                               nulls[3] = 'n';
+                               nulls[4] = 'n';
+                               nulls[5] = 'n';
+                               nulls[7] = 'n';
+                               nulls[8] = 'n';
+                               nulls[9] = 'n';
+                               break;
+                       case LOCKTAG_VIRTUALTRANSACTION:
+                               values[5] = VXIDGetDatum(lock->tag.locktag_field1,
+                                                                                lock->tag.locktag_field2);
+                               nulls[1] = 'n';
+                               nulls[2] = 'n';
+                               nulls[3] = 'n';
+                               nulls[4] = 'n';
+                               nulls[6] = 'n';
+                               nulls[7] = 'n';
+                               nulls[8] = 'n';
+                               nulls[9] = 'n';
+                               break;
+                       case LOCKTAG_OBJECT:
+                       case LOCKTAG_USERLOCK:
+                       case LOCKTAG_ADVISORY:
+                       default:                        /* treat unknown locktags like OBJECT */
+                               values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
+                               values[7] = ObjectIdGetDatum(lock->tag.locktag_field2);
+                               values[8] = ObjectIdGetDatum(lock->tag.locktag_field3);
+                               values[9] = Int16GetDatum(lock->tag.locktag_field4);
+                               nulls[2] = 'n';
+                               nulls[3] = 'n';
+                               nulls[4] = 'n';
+                               nulls[5] = 'n';
+                               nulls[6] = 'n';
+                               break;
                }
 
-               values[3] = Int32GetDatum(proc->pid);
-               values[4] = DirectFunctionCall1(textin,
-                                                                CStringGetDatum(GetLockmodeName(mode)));
-               values[5] = BoolGetDatum(granted);
+               values[10] = VXIDGetDatum(proc->backendId, proc->lxid);
+               if (proc->pid != 0)
+                       values[11] = Int32GetDatum(proc->pid);
+               else
+                       nulls[11] = 'n';
+               values[12] = DirectFunctionCall1(textin,
+                                         CStringGetDatum(GetLockmodeName(LOCK_LOCKMETHOD(*lock),
+                                                                                                         mode)));
+               values[13] = BoolGetDatum(granted);
 
-               tuple = heap_formtuple(funcctx->slot->ttc_tupleDescriptor,
-                                                          values, nulls);
-               result = TupleGetDatum(funcctx->slot, tuple);
+               tuple = heap_formtuple(funcctx->tuple_desc, values, nulls);
+               result = HeapTupleGetDatum(tuple);
                SRF_RETURN_NEXT(funcctx, result);
        }
 
        SRF_RETURN_DONE(funcctx);
 }
+
+
+/*
+ * Functions for manipulating advisory locks
+ *
+ * We make use of the locktag fields as follows:
+ *
+ *     field1: MyDatabaseId ... ensures locks are local to each database
+ *     field2: first of 2 int4 keys, or high-order half of an int8 key
+ *     field3: second of 2 int4 keys, or low-order half of an int8 key
+ *     field4: 1 if using an int8 key, 2 if using 2 int4 keys
+ */
+#define SET_LOCKTAG_INT64(tag, key64) \
+       SET_LOCKTAG_ADVISORY(tag, \
+                                                MyDatabaseId, \
+                                                (uint32) ((key64) >> 32), \
+                                                (uint32) (key64), \
+                                                1)
+#define SET_LOCKTAG_INT32(tag, key1, key2) \
+       SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, key1, key2, 2)
+
+/*
+ * pg_advisory_lock(int8) - acquire exclusive lock on an int8 key
+ */
+Datum
+pg_advisory_lock_int8(PG_FUNCTION_ARGS)
+{
+       int64           key = PG_GETARG_INT64(0);
+       LOCKTAG         tag;
+
+       SET_LOCKTAG_INT64(tag, key);
+
+       (void) LockAcquire(&tag, ExclusiveLock, true, false);
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * pg_advisory_lock_shared(int8) - acquire share lock on an int8 key
+ */
+Datum
+pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
+{
+       int64           key = PG_GETARG_INT64(0);
+       LOCKTAG         tag;
+
+       SET_LOCKTAG_INT64(tag, key);
+
+       (void) LockAcquire(&tag, ShareLock, true, false);
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * pg_try_advisory_lock(int8) - acquire exclusive lock on an int8 key, no wait
+ *
+ * Returns true if successful, false if lock not available
+ */
+Datum
+pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
+{
+       int64           key = PG_GETARG_INT64(0);
+       LOCKTAG         tag;
+       LockAcquireResult res;
+
+       SET_LOCKTAG_INT64(tag, key);
+
+       res = LockAcquire(&tag, ExclusiveLock, true, true);
+
+       PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
+}
+
+/*
+ * pg_try_advisory_lock_shared(int8) - acquire share lock on an int8 key, no wait
+ *
+ * Returns true if successful, false if lock not available
+ */
+Datum
+pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
+{
+       int64           key = PG_GETARG_INT64(0);
+       LOCKTAG         tag;
+       LockAcquireResult res;
+
+       SET_LOCKTAG_INT64(tag, key);
+
+       res = LockAcquire(&tag, ShareLock, true, true);
+
+       PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
+}
+
+/*
+ * pg_advisory_unlock(int8) - release exclusive lock on an int8 key
+ *
+ * Returns true if successful, false if lock was not held
+*/
+Datum
+pg_advisory_unlock_int8(PG_FUNCTION_ARGS)
+{
+       int64           key = PG_GETARG_INT64(0);
+       LOCKTAG         tag;
+       bool            res;
+
+       SET_LOCKTAG_INT64(tag, key);
+
+       res = LockRelease(&tag, ExclusiveLock, true);
+
+       PG_RETURN_BOOL(res);
+}
+
+/*
+ * pg_advisory_unlock_shared(int8) - release share lock on an int8 key
+ *
+ * Returns true if successful, false if lock was not held
+ */
+Datum
+pg_advisory_unlock_shared_int8(PG_FUNCTION_ARGS)
+{
+       int64           key = PG_GETARG_INT64(0);
+       LOCKTAG         tag;
+       bool            res;
+
+       SET_LOCKTAG_INT64(tag, key);
+
+       res = LockRelease(&tag, ShareLock, true);
+
+       PG_RETURN_BOOL(res);
+}
+
+/*
+ * pg_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys
+ */
+Datum
+pg_advisory_lock_int4(PG_FUNCTION_ARGS)
+{
+       int32           key1 = PG_GETARG_INT32(0);
+       int32           key2 = PG_GETARG_INT32(1);
+       LOCKTAG         tag;
+
+       SET_LOCKTAG_INT32(tag, key1, key2);
+
+       (void) LockAcquire(&tag, ExclusiveLock, true, false);
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys
+ */
+Datum
+pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
+{
+       int32           key1 = PG_GETARG_INT32(0);
+       int32           key2 = PG_GETARG_INT32(1);
+       LOCKTAG         tag;
+
+       SET_LOCKTAG_INT32(tag, key1, key2);
+
+       (void) LockAcquire(&tag, ShareLock, true, false);
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * pg_try_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys, no wait
+ *
+ * Returns true if successful, false if lock not available
+ */
+Datum
+pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
+{
+       int32           key1 = PG_GETARG_INT32(0);
+       int32           key2 = PG_GETARG_INT32(1);
+       LOCKTAG         tag;
+       LockAcquireResult res;
+
+       SET_LOCKTAG_INT32(tag, key1, key2);
+
+       res = LockAcquire(&tag, ExclusiveLock, true, true);
+
+       PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
+}
+
+/*
+ * pg_try_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys, no wait
+ *
+ * Returns true if successful, false if lock not available
+ */
+Datum
+pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
+{
+       int32           key1 = PG_GETARG_INT32(0);
+       int32           key2 = PG_GETARG_INT32(1);
+       LOCKTAG         tag;
+       LockAcquireResult res;
+
+       SET_LOCKTAG_INT32(tag, key1, key2);
+
+       res = LockAcquire(&tag, ShareLock, true, true);
+
+       PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
+}
+
+/*
+ * pg_advisory_unlock(int4, int4) - release exclusive lock on 2 int4 keys
+ *
+ * Returns true if successful, false if lock was not held
+*/
+Datum
+pg_advisory_unlock_int4(PG_FUNCTION_ARGS)
+{
+       int32           key1 = PG_GETARG_INT32(0);
+       int32           key2 = PG_GETARG_INT32(1);
+       LOCKTAG         tag;
+       bool            res;
+
+       SET_LOCKTAG_INT32(tag, key1, key2);
+
+       res = LockRelease(&tag, ExclusiveLock, true);
+
+       PG_RETURN_BOOL(res);
+}
+
+/*
+ * pg_advisory_unlock_shared(int4, int4) - release share lock on 2 int4 keys
+ *
+ * Returns true if successful, false if lock was not held
+ */
+Datum
+pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
+{
+       int32           key1 = PG_GETARG_INT32(0);
+       int32           key2 = PG_GETARG_INT32(1);
+       LOCKTAG         tag;
+       bool            res;
+
+       SET_LOCKTAG_INT32(tag, key1, key2);
+
+       res = LockRelease(&tag, ShareLock, true);
+
+       PG_RETURN_BOOL(res);
+}
+
+/*
+ * pg_advisory_unlock_all() - release all advisory locks
+ */
+Datum
+pg_advisory_unlock_all(PG_FUNCTION_ARGS)
+{
+       LockReleaseAll(USER_LOCKMETHOD, true);
+
+       PG_RETURN_VOID();
+}