diff options
Diffstat (limited to 'src/backend/utils/adt/lockfuncs.c')
-rw-r--r-- | src/backend/utils/adt/lockfuncs.c | 186 |
1 files changed, 102 insertions, 84 deletions
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c index 199efbacd26..b1ccceebcb9 100644 --- a/src/backend/utils/adt/lockfuncs.c +++ b/src/backend/utils/adt/lockfuncs.c @@ -1,36 +1,46 @@ -/* +/*------------------------------------------------------------------------- + * * lockfuncs.c * Set-returning functions to view the state of locks within the DB. * * Copyright (c) 2002, PostgreSQL Global Development Group * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.4 2002/08/29 17:14:33 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.5 2002/08/31 17:14:28 tgl Exp $ + * + *------------------------------------------------------------------------- */ #include "postgres.h" #include "funcapi.h" +#include "access/heapam.h" #include "catalog/pg_type.h" -#include "storage/lmgr.h" #include "storage/lock.h" -#include "storage/lwlock.h" #include "storage/proc.h" #include "utils/builtins.h" -static int next_lock(int locks[]); - +/* Working status for pg_lock_status */ +typedef struct +{ + LockData *lockData; /* state data from lmgr */ + int currIdx; /* current PROCLOCK index */ +} PG_Lock_Status; +/* + * pg_lock_status - produce a view with one row per held or awaited lock mode + */ Datum pg_lock_status(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; - LockData *lockData; - MemoryContext oldcontext; + PG_Lock_Status *mystatus; + LockData *lockData; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; + MemoryContext oldcontext; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); @@ -38,124 +48,132 @@ pg_lock_status(PG_FUNCTION_ARGS) /* switch to memory context appropriate for multiple function calls */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - tupdesc = CreateTemplateTupleDesc(5, WITHOUTOID); + /* build tupdesc for result tuples */ + /* this had better match pg_locks view in initdb.sh */ + tupdesc = CreateTemplateTupleDesc(6, WITHOUTOID); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation", OIDOID, -1, 0, false); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database", OIDOID, -1, 0, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 3, "backendpid", + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "transaction", + XIDOID, -1, 0, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pid", INT4OID, -1, 0, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 4, "mode", + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "mode", TEXTOID, -1, 0, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 5, "isgranted", + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "granted", BOOLOID, -1, 0, false); funcctx->slot = TupleDescGetSlot(tupdesc); - funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); /* - * 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. + * Collect all the locking information that we will format + * and send out as a result set. */ - funcctx->user_fctx = (LockData *) palloc(sizeof(LockData)); + mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status)); + funcctx->user_fctx = (void *) mystatus; - GetLockStatusData(funcctx->user_fctx); + mystatus->lockData = GetLockStatusData(); + mystatus->currIdx = 0; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); - lockData = (LockData *) funcctx->user_fctx; + mystatus = (PG_Lock_Status *) funcctx->user_fctx; + lockData = mystatus->lockData; - while (lockData->currIdx < lockData->nelements) + while (mystatus->currIdx < lockData->nelements) { PROCLOCK *holder; LOCK *lock; PGPROC *proc; + bool granted; + LOCKMODE mode; + Datum values[6]; + char nulls[6]; 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 = funcctx->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); + holder = &(lockData->holders[mystatus->currIdx]); + lock = &(lockData->locks[mystatus->currIdx]); + proc = &(lockData->procs[mystatus->currIdx]); /* - * 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. + * 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. */ - if (holder->nHolding > 0) + granted = false; + for (mode = 0; mode < MAX_LOCKMODES; mode++) { - /* Already held locks */ - mode = next_lock(holder->holding); - holder->holding[mode]--; - holder->nHolding--; - - strcpy(values[4], "t"); + if (holder->holding[mode] > 0) + { + granted = true; + holder->holding[mode] = 0; + break; + } } - else if (proc->waitLock != NULL) + + /* + * If no (more) held modes to report, see if PROC is waiting for + * a lock on this lock. + */ + if (!granted) { - /* Lock that is still being waited on */ - mode = proc->waitLockMode; - proc->waitLock = NULL; - proc->waitLockMode = NoLock; + if (proc->waitLock == (LOCK *) MAKE_PTR(holder->tag.lock)) + { + /* 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. + */ + mystatus->currIdx++; + } + else + { + /* + * Okay, we've displayed all the locks associated with this + * PROCLOCK, proceed to the next one. + */ + mystatus->currIdx++; + continue; + } + } - strcpy(values[4], "f"); + /* + * Form tuple with appropriate data. + */ + 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); } else { - /* - * Okay, we've displayed all the lock's belonging to this PROCLOCK, - * procede to the next one. - */ - lockData->currIdx++; - continue; + /* Lock is for a relation */ + values[0] = ObjectIdGetDatum(lock->tag.relId); + values[1] = ObjectIdGetDatum(lock->tag.dbId); + nulls[2] = 'n'; + } - strncpy(values[3], GetLockmodeName(mode), 32); + values[3] = Int32GetDatum(proc->pid); + values[4] = DirectFunctionCall1(textin, + CStringGetDatum(GetLockmodeName(mode))); + values[5] = BoolGetDatum(granted); - tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); + tuple = heap_formtuple(funcctx->slot->ttc_tupleDescriptor, + values, nulls); result = TupleGetDatum(funcctx->slot, tuple); SRF_RETURN_NEXT(funcctx, result); } SRF_RETURN_DONE(funcctx); } - -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; -} |