/*-------------------------------------------------------------------------
*
* 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
{
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
*/
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;
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;
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;
+ }
}
}
*/
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;
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();
+}