2002-08-17 15:11:43 +02:00
|
|
|
/*
|
|
|
|
* lockfuncs.c
|
|
|
|
* Set-returning functions to view the state of locks within the DB.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2002, PostgreSQL Global Development Group
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2002-08-29 02:17:06 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.3 2002/08/29 00:17:05 tgl Exp $
|
2002-08-17 15:11:43 +02:00
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
2002-08-29 02:17:06 +02:00
|
|
|
|
2002-08-17 15:11:43 +02:00
|
|
|
#include "funcapi.h"
|
2002-08-27 06:00:28 +02:00
|
|
|
#include "catalog/pg_type.h"
|
2002-08-17 15:11:43 +02:00
|
|
|
#include "storage/lmgr.h"
|
|
|
|
#include "storage/lock.h"
|
|
|
|
#include "storage/lwlock.h"
|
|
|
|
#include "storage/proc.h"
|
2002-08-29 02:17:06 +02:00
|
|
|
#include "utils/builtins.h"
|
2002-08-17 15:11:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
static int next_lock(int locks[]);
|
|
|
|
|
2002-08-29 02:17:06 +02:00
|
|
|
|
2002-08-17 15:11:43 +02:00
|
|
|
Datum
|
2002-08-27 06:00:28 +02:00
|
|
|
pg_lock_status(PG_FUNCTION_ARGS)
|
2002-08-17 15:11:43 +02:00
|
|
|
{
|
|
|
|
FuncCallContext *funccxt;
|
|
|
|
LockData *lockData;
|
|
|
|
|
|
|
|
if (SRF_IS_FIRSTCALL())
|
|
|
|
{
|
|
|
|
MemoryContext oldcxt;
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
|
|
|
|
funccxt = SRF_FIRSTCALL_INIT();
|
2002-08-27 06:00:28 +02:00
|
|
|
tupdesc = CreateTemplateTupleDesc(5, WITHOUTOID);
|
|
|
|
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation",
|
|
|
|
OIDOID, -1, 0, false);
|
|
|
|
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
|
|
|
|
OIDOID, -1, 0, false);
|
|
|
|
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "backendpid",
|
|
|
|
INT4OID, -1, 0, false);
|
|
|
|
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "mode",
|
|
|
|
TEXTOID, -1, 0, false);
|
|
|
|
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "isgranted",
|
|
|
|
BOOLOID, -1, 0, false);
|
|
|
|
|
2002-08-17 15:11:43 +02:00
|
|
|
funccxt->slot = TupleDescGetSlot(tupdesc);
|
|
|
|
funccxt->attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
|
|
|
|
|
|
|
oldcxt = MemoryContextSwitchTo(funccxt->fmctx);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Preload all the locking information that we will eventually format
|
|
|
|
* and send out as a result set. This is palloc'ed, but since the
|
|
|
|
* MemoryContext is reset when the SRF finishes, we don't need to
|
|
|
|
* free it ourselves.
|
|
|
|
*/
|
|
|
|
funccxt->user_fctx = (LockData *) palloc(sizeof(LockData));
|
|
|
|
|
|
|
|
GetLockStatusData(funccxt->user_fctx);
|
|
|
|
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
|
|
}
|
|
|
|
|
|
|
|
funccxt = SRF_PERCALL_SETUP();
|
|
|
|
lockData = (LockData *) funccxt->user_fctx;
|
|
|
|
|
|
|
|
while (lockData->currIdx < lockData->nelements)
|
|
|
|
{
|
|
|
|
PROCLOCK *holder;
|
|
|
|
LOCK *lock;
|
|
|
|
PGPROC *proc;
|
|
|
|
HeapTuple tuple;
|
|
|
|
Datum result;
|
|
|
|
char **values;
|
|
|
|
LOCKMODE mode;
|
|
|
|
int num_attrs;
|
|
|
|
int i;
|
|
|
|
int currIdx = lockData->currIdx;
|
|
|
|
|
|
|
|
holder = &(lockData->holders[currIdx]);
|
|
|
|
lock = &(lockData->locks[currIdx]);
|
|
|
|
proc = &(lockData->procs[currIdx]);
|
|
|
|
num_attrs = funccxt->attinmeta->tupdesc->natts;
|
|
|
|
|
|
|
|
values = (char **) palloc(sizeof(*values) * num_attrs);
|
|
|
|
|
|
|
|
for (i = 0; i < num_attrs; i++)
|
|
|
|
values[i] = (char *) palloc(32);
|
|
|
|
|
|
|
|
/* The OID of the locked relation */
|
|
|
|
snprintf(values[0], 32, "%u", lock->tag.relId);
|
|
|
|
/* The database the relation is in */
|
|
|
|
snprintf(values[1], 32, "%u", lock->tag.dbId);
|
|
|
|
/* The PID of the backend holding or waiting for the lock */
|
|
|
|
snprintf(values[2], 32, "%d", proc->pid);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to report both the locks held (i.e. successfully acquired)
|
|
|
|
* by this holder, as well as the locks upon which it is still
|
|
|
|
* waiting, if any. Since a single PROCLOCK struct may contain
|
|
|
|
* multiple locks, we may need to loop several times before we
|
|
|
|
* advance the array index and continue on.
|
|
|
|
*/
|
|
|
|
if (holder->nHolding > 0)
|
|
|
|
{
|
|
|
|
/* Already held locks */
|
|
|
|
mode = next_lock(holder->holding);
|
|
|
|
holder->holding[mode]--;
|
|
|
|
holder->nHolding--;
|
|
|
|
|
|
|
|
strcpy(values[4], "t");
|
|
|
|
}
|
|
|
|
else if (proc->waitLock != NULL)
|
|
|
|
{
|
|
|
|
/* Lock that is still being waited on */
|
|
|
|
mode = proc->waitLockMode;
|
|
|
|
proc->waitLock = NULL;
|
|
|
|
proc->waitLockMode = NoLock;
|
|
|
|
|
|
|
|
strcpy(values[4], "f");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Okay, we've displayed all the lock's belonging to this PROCLOCK,
|
|
|
|
* procede to the next one.
|
|
|
|
*/
|
|
|
|
lockData->currIdx++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(values[3], GetLockmodeName(mode), 32);
|
|
|
|
|
|
|
|
tuple = BuildTupleFromCStrings(funccxt->attinmeta, values);
|
|
|
|
result = TupleGetDatum(funccxt->slot, tuple);
|
|
|
|
SRF_RETURN_NEXT(funccxt, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
SRF_RETURN_DONE(funccxt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static LOCKMODE
|
|
|
|
next_lock(int locks[])
|
|
|
|
{
|
|
|
|
LOCKMODE i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_LOCKMODES; i++)
|
|
|
|
{
|
|
|
|
if (locks[i] != 0)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No locks found: this should not occur */
|
|
|
|
Assert(false);
|
|
|
|
return -1;
|
|
|
|
}
|