Index: src/backend/storage/lmgr/lock.c =================================================================== RCS file: /var/lib/cvs/pgsql/src/backend/storage/lmgr/lock.c,v retrieving revision 1.110 diff -c -r1.110 lock.c *** src/backend/storage/lmgr/lock.c 19 Jul 2002 00:17:40 -0000 1.110 --- src/backend/storage/lmgr/lock.c 23 Jul 2002 22:41:20 -0000 *************** *** 1364,1369 **** --- 1364,1429 ---- return size; } + /* + * GetLockStatusData - Return a summary of the lock manager's internal + * status, for use in a user-level statistical reporting function. + * + * This function should be passed a pointer to a LockData struct. It fills + * the structure with the appropriate information and returns. The goal + * is to hold the LockMgrLock for as short a time as possible; thus, the + * function simply makes a copy of the necessary data and releases the + * lock, allowing the caller to contemplate and format the data for + * as long as it pleases. + */ + void + GetLockStatusData(LockData *data) + { + HTAB *holderTable; + PROCLOCK *holder; + HASH_SEQ_STATUS seqstat; + int i = 0; + + data->currIdx = 0; + + LWLockAcquire(LockMgrLock, LW_EXCLUSIVE); + + holderTable = LockMethodTable[DEFAULT_LOCKMETHOD]->holderHash; + + data->nelements = holderTable->hctl->nentries; + + data->procs = (PGPROC *) palloc(sizeof(PGPROC) * data->nelements); + data->locks = (LOCK *) palloc(sizeof(LOCK) * data->nelements); + data->holders = (PROCLOCK *) palloc(sizeof(PROCLOCK) * data->nelements); + + hash_seq_init(&seqstat, holderTable); + + while ( (holder = hash_seq_search(&seqstat)) ) + { + PGPROC *proc; + LOCK *lock; + + /* Only do a shallow copy */ + proc = (PGPROC *) MAKE_PTR(holder->tag.proc); + lock = (LOCK *) MAKE_PTR(holder->tag.lock); + + memcpy(&(data->procs[i]), proc, sizeof(PGPROC)); + memcpy(&(data->locks[i]), lock, sizeof(LOCK)); + memcpy(&(data->holders[i]), holder, sizeof(PROCLOCK)); + + i++; + } + + Assert(i == data->nelements); + + LWLockRelease(LockMgrLock); + } + + char * + GetLockmodeName(LOCKMODE mode) + { + Assert(mode <= MAX_LOCKMODES); + return lock_mode_names[mode]; + } #ifdef LOCK_DEBUG /* Index: src/backend/tcop/utility.c =================================================================== RCS file: /var/lib/cvs/pgsql/src/backend/tcop/utility.c,v retrieving revision 1.164 diff -c -r1.164 utility.c *** src/backend/tcop/utility.c 18 Jul 2002 23:11:28 -0000 1.164 --- src/backend/tcop/utility.c 23 Jul 2002 22:41:20 -0000 *************** *** 217,224 **** break; /* ! * ******************************** portal manipulation ******************************** ! * */ case T_ClosePortalStmt: { --- 217,223 ---- break; /* ! * ************************* portal manipulation *************************** */ case T_ClosePortalStmt: { Index: src/backend/utils/adt/Makefile =================================================================== RCS file: /var/lib/cvs/pgsql/src/backend/utils/adt/Makefile,v retrieving revision 1.51 diff -c -r1.51 Makefile *** src/backend/utils/adt/Makefile 4 Oct 2001 04:13:40 -0000 1.51 --- src/backend/utils/adt/Makefile 23 Jul 2002 22:41:20 -0000 *************** *** 17,23 **** OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o \ date.o datetime.o datum.o float.o format_type.o \ ! geo_ops.o geo_selfuncs.o int.o int8.o like.o \ misc.o nabstime.o name.o not_in.o numeric.o numutils.o \ oid.o oracle_compat.o \ regexp.o regproc.o ruleutils.o selfuncs.o sets.o \ --- 17,23 ---- OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o \ date.o datetime.o datum.o float.o format_type.o \ ! geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \ misc.o nabstime.o name.o not_in.o numeric.o numutils.o \ oid.o oracle_compat.o \ regexp.o regproc.o ruleutils.o selfuncs.o sets.o \ Index: src/backend/utils/adt/lockfuncs.c =================================================================== RCS file: src/backend/utils/adt/lockfuncs.c diff -N src/backend/utils/adt/lockfuncs.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/backend/utils/adt/lockfuncs.c 23 Jul 2002 22:41:20 -0000 *************** *** 0 **** --- 1,146 ---- + /* + * lockfuncs.c + * Set-returning functions to view the state of locks within the DB. + * + * Copyright (c) 2002, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $Header$ + */ + + #include "postgres.h" + #include "fmgr.h" + #include "funcapi.h" + #include "storage/lmgr.h" + #include "storage/lock.h" + #include "storage/lwlock.h" + #include "storage/proc.h" + + Datum lock_info_srf(PG_FUNCTION_ARGS); + + static int next_lock(int locks[]); + + Datum + lock_info_srf(PG_FUNCTION_ARGS) + { + FuncCallContext *funccxt; + LockData *lockData; + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcxt; + TupleDesc tupdesc; + + funccxt = SRF_FIRSTCALL_INIT(); + tupdesc = RelationNameGetTupleDesc("pg_catalog.pg_locks_result"); + 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; + } Index: src/bin/initdb/initdb.sh =================================================================== RCS file: /var/lib/cvs/pgsql/src/bin/initdb/initdb.sh,v retrieving revision 1.161 diff -c -r1.161 initdb.sh *** src/bin/initdb/initdb.sh 18 Jul 2002 23:11:29 -0000 1.161 --- src/bin/initdb/initdb.sh 23 Jul 2002 22:41:20 -0000 *************** *** 764,771 **** FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) \ WHERE C.relkind = 'v'; - -- XXX why does pg_tables include sequences? - CREATE VIEW pg_tables AS \ SELECT \ C.relname AS tablename, \ --- 764,769 ---- *************** *** 970,975 **** --- 968,986 ---- pg_stat_get_db_blocks_hit(D.oid) AS blks_read, \ pg_stat_get_db_blocks_hit(D.oid) AS blks_hit \ FROM pg_database D; + + CREATE VIEW pg_locks_result AS \ + SELECT \ + ''::oid AS relation, \ + ''::oid AS database, \ + ''::int4 AS backendpid, \ + ''::text AS mode, \ + NULL::bool AS isgranted; + + UPDATE pg_proc SET \ + prorettype = (SELECT oid FROM pg_type \ + WHERE typname = 'pg_locks_result') \ + WHERE proname = 'pg_locks'; EOF if [ "$?" -ne 0 ]; then Index: src/include/catalog/catversion.h =================================================================== RCS file: /var/lib/cvs/pgsql/src/include/catalog/catversion.h,v retrieving revision 1.141 diff -c -r1.141 catversion.h *** src/include/catalog/catversion.h 18 Jul 2002 23:11:30 -0000 1.141 --- src/include/catalog/catversion.h 23 Jul 2002 22:41:20 -0000 *************** *** 53,58 **** */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200207191 #endif --- 53,58 ---- */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200207231 #endif Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /var/lib/cvs/pgsql/src/include/catalog/pg_proc.h,v retrieving revision 1.245 diff -c -r1.245 pg_proc.h *** src/include/catalog/pg_proc.h 20 Jul 2002 05:49:27 -0000 1.245 --- src/include/catalog/pg_proc.h 23 Jul 2002 22:41:20 -0000 *************** *** 2681,2686 **** --- 2681,2689 ---- DATA(insert OID = 1915 ( numeric_uplus PGNSP PGUID 12 f f f t f i 1 1700 "1700" 100 0 0 100 numeric_uplus - _null_ )); DESCR("unary plus"); + DATA(insert OID = 1920 ( pg_locks PGNSP PGUID 12 f f f t t v 0 0 "0" 100 0 0 100 lock_info_srf - _null_ )); + DESCR("view system lock information"); + DATA(insert OID = 1922 ( has_table_privilege PGNSP PGUID 12 f f f t f s 3 16 "19 25 25" 100 0 0 100 has_table_privilege_name_name - _null_ )); DESCR("user privilege on relation by username, relname"); DATA(insert OID = 1923 ( has_table_privilege PGNSP PGUID 12 f f f t f s 3 16 "19 26 25" 100 0 0 100 has_table_privilege_name_id - _null_ )); Index: src/include/storage/lock.h =================================================================== RCS file: /var/lib/cvs/pgsql/src/include/storage/lock.h,v retrieving revision 1.63 diff -c -r1.63 lock.h *** src/include/storage/lock.h 19 Jul 2002 00:17:40 -0000 1.63 --- src/include/storage/lock.h 23 Jul 2002 22:41:20 -0000 *************** *** 208,213 **** --- 208,228 ---- #define PROCLOCK_LOCKMETHOD(holder) \ (((LOCK *) MAKE_PTR((holder).tag.lock))->tag.lockmethod) + /* + * This struct is used to encapsulate information passed from lmgr + * internals to the lock listing statistical functions (lockfuncs.c). + * It's just a convenient bundle of other lock.h structures. All + * the information at a given index (holders[i], procs[i], locks[i]) + * is related. + */ + typedef struct + { + int nelements; /* The length of holders, procs, & locks */ + int currIdx; /* Current element being examined */ + PGPROC *procs; + LOCK *locks; + PROCLOCK *holders; + } LockData; /* * function prototypes *************** *** 232,237 **** --- 247,254 ---- extern int LockShmemSize(int maxBackends); extern bool DeadLockCheck(PGPROC *proc); extern void InitDeadLockChecking(void); + extern void GetLockStatusData(LockData *data); + extern char *GetLockmodeName(LOCKMODE mode); #ifdef LOCK_DEBUG extern void DumpLocks(void); Index: src/include/storage/shmem.h =================================================================== RCS file: /var/lib/cvs/pgsql/src/include/storage/shmem.h,v retrieving revision 1.37 diff -c -r1.37 shmem.h *** src/include/storage/shmem.h 20 Jun 2002 20:29:52 -0000 1.37 --- src/include/storage/shmem.h 23 Jul 2002 22:41:20 -0000 *************** *** 53,60 **** #define SHM_OFFSET_VALID(xx_offs)\ (((xx_offs) != 0) && ((xx_offs) != INVALID_OFFSET)) ! ! /* shmemqueue.c */ typedef struct SHM_QUEUE { SHMEM_OFFSET prev; --- 53,59 ---- #define SHM_OFFSET_VALID(xx_offs)\ (((xx_offs) != 0) && ((xx_offs) != INVALID_OFFSET)) ! /* shmqueue.c */ typedef struct SHM_QUEUE { SHMEM_OFFSET prev; Index: src/test/regress/expected/rules.out =================================================================== RCS file: /var/lib/cvs/pgsql/src/test/regress/expected/rules.out,v retrieving revision 1.53 diff -c -r1.53 rules.out *** src/test/regress/expected/rules.out 3 May 2002 00:32:19 -0000 1.53 --- src/test/regress/expected/rules.out 23 Jul 2002 22:41:20 -0000 *************** *** 1268,1273 **** --- 1268,1274 ---- --------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); pg_indexes | SELECT c.relname AS tablename, i.relname AS indexname, pg_get_indexdef(x.indexrelid) AS indexdef FROM pg_index x, pg_class c, pg_class i WHERE ((((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")) AND (c.oid = x.indrelid)) AND (i.oid = x.indexrelid)); + pg_locks_result | SELECT 0::oid AS relation, 0::oid AS "database", 0 AS backendpid, ''::text AS "mode", NULL::boolean AS isgranted; pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name); pg_stat_activity | SELECT d.oid AS datid, d.datname, pg_stat_get_backend_pid(s.backendid) AS procpid, pg_stat_get_backend_userid(s.backendid) AS usesysid, u.usename, pg_stat_get_backend_activity(s.backendid) AS current_query FROM pg_database d, (SELECT pg_stat_get_backend_idset() AS backendid) s, pg_shadow u WHERE ((pg_stat_get_backend_dbid(s.backendid) = d.oid) AND (pg_stat_get_backend_userid(s.backendid) = u.usesysid)); pg_stat_all_indexes | SELECT c.oid AS relid, i.oid AS indexrelid, c.relname, i.relname AS indexrelname, pg_stat_get_numscans(i.oid) AS idx_scan, pg_stat_get_tuples_returned(i.oid) AS idx_tup_read, pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch FROM pg_class c, pg_class i, pg_index x WHERE (((c.relkind = 'r'::"char") AND (x.indrelid = c.oid)) AND (x.indexrelid = i.oid)); *************** *** 1304,1310 **** shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); toyemp | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp; ! (38 rows) SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename; --- 1305,1311 ---- shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); toyemp | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp; ! (39 rows) SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename;