| /*------------------------------------------------------------------------- |
| * |
| * lockfuncs.c |
| * Functions for SQL access to various lock-manager capabilities. |
| * |
| * Copyright (c) 2002-2009, PostgreSQL Global Development Group |
| * |
| * IDENTIFICATION |
| * $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.27 2006/10/04 00:29:59 momjian Exp $ |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include "access/heapam.h" |
| #include "catalog/pg_type.h" |
| #include "funcapi.h" |
| #include "miscadmin.h" |
| #include "storage/proc.h" |
| #include "utils/builtins.h" |
| |
| #include "gp-libpq-fe.h" |
| #include "cdb/cdbdisp.h" |
| #include "cdb/cdbvars.h" |
| #include "cdb/dispatcher.h" |
| |
| |
| /* This must match enum LockTagType! */ |
| static const char *const LockTagTypeNames[] = { |
| "relation", |
| "extend", |
| "page", |
| "tuple", |
| "transactionid", |
| "resynchronize", |
| "append-only segment file", |
| "object", |
| "resource queue", |
| "userlock", |
| "advisory" |
| }; |
| |
| /* Working status for pg_lock_status */ |
| typedef struct |
| { |
| LockData *lockData; /* state data from lmgr */ |
| int currIdx; /* current PROCLOCK index */ |
| int numSegLocks; /* Total number of locks being reported back to client */ |
| int numsegresults; /* If we dispatch to segDBs, the number of segresults */ |
| struct pg_result **segresults; /* pg_result for each segDB */ |
| |
| } 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; |
| 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(); |
| |
| /* |
| * 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 system_views.sql */ |
| tupdesc = CreateTemplateTupleDesc(16, false); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype", |
| TEXTOID, -1, 0); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database", |
| 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, "transactionid", |
| XIDOID, -1, 0); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 7, "classid", |
| OIDOID, -1, 0); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 8, "objid", |
| OIDOID, -1, 0); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objsubid", |
| INT2OID, -1, 0); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 10, "transaction", |
| XIDOID, -1, 0); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 11, "pid", |
| INT4OID, -1, 0); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 12, "mode", |
| TEXTOID, -1, 0); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 13, "granted", |
| BOOLOID, -1, 0); |
| /* |
| * These next columns are specific to GPDB |
| */ |
| TupleDescInitEntry(tupdesc, (AttrNumber) 14, "mppSessionId", |
| INT4OID, -1, 0); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 15, "mppIsWriter", |
| BOOLOID, -1, 0); |
| TupleDescInitEntry(tupdesc, (AttrNumber) 16, "gp_segment_id", |
| INT4OID, -1, 0); |
| |
| funcctx->tuple_desc = BlessTupleDesc(tupdesc); |
| |
| /* |
| * 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; |
| |
| mystatus->lockData = GetLockStatusData(); |
| mystatus->currIdx = 0; |
| mystatus->numSegLocks = 0; |
| mystatus->numsegresults = 0; |
| mystatus->segresults = NULL; |
| |
| MemoryContextSwitchTo(oldcontext); |
| } |
| |
| funcctx = SRF_PERCALL_SETUP(); |
| mystatus = (PG_Lock_Status *) funcctx->user_fctx; |
| lockData = mystatus->lockData; |
| |
| /* |
| * This loop returns all the local lock data from the segment we are running on. |
| */ |
| |
| while (mystatus->currIdx < lockData->nelements) |
| { |
| PROCLOCK *proclock; |
| LOCK *lock; |
| PGPROC *proc; |
| bool granted; |
| LOCKMODE mode = 0; |
| const char *locktypename; |
| char tnbuf[32]; |
| Datum values[16]; |
| bool nulls[16]; |
| HeapTuple tuple; |
| Datum result; |
| |
| proclock = &(lockData->proclocks[mystatus->currIdx]); |
| lock = &(lockData->locks[mystatus->currIdx]); |
| 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. |
| */ |
| granted = false; |
| if (proclock->holdMask) |
| { |
| for (mode = 0; mode < MAX_LOCKMODES; mode++) |
| { |
| if (proclock->holdMask & LOCKBIT_ON(mode)) |
| { |
| granted = true; |
| proclock->holdMask &= LOCKBIT_OFF(mode); |
| break; |
| } |
| } |
| } |
| |
| /* |
| * If no (more) held modes to report, see if PROC is waiting for a |
| * lock on this lock. |
| */ |
| if (!granted) |
| { |
| 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. |
| */ |
| mystatus->currIdx++; |
| } |
| else |
| { |
| /* |
| * Okay, we've displayed all the locks associated with this |
| * PROCLOCK, proceed to the next one. |
| */ |
| mystatus->currIdx++; |
| continue; |
| } |
| } |
| |
| /* |
| * Form tuple with appropriate data. |
| */ |
| MemSet(values, 0, sizeof(values)); |
| MemSet(nulls, false, sizeof(nulls)); |
| |
| if (lock->tag.locktag_type <= LOCKTAG_ADVISORY) |
| locktypename = LockTagTypeNames[lock->tag.locktag_type]; |
| else |
| { |
| snprintf(tnbuf, sizeof(tnbuf), "unknown %d", |
| (int) lock->tag.locktag_type); |
| locktypename = tnbuf; |
| } |
| values[0] = CStringGetTextDatum(locktypename); |
| |
| switch (lock->tag.locktag_type) |
| { |
| case LOCKTAG_RELATION: |
| case LOCKTAG_RELATION_EXTEND: |
| case LOCKTAG_RELATION_RESYNCHRONIZE: |
| values[1] = ObjectIdGetDatum(lock->tag.locktag_field1); |
| values[2] = ObjectIdGetDatum(lock->tag.locktag_field2); |
| nulls[3] = true; |
| nulls[4] = true; |
| nulls[5] = true; |
| nulls[6] = true; |
| nulls[7] = true; |
| nulls[8] = true; |
| 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] = true; |
| nulls[5] = true; |
| nulls[6] = true; |
| nulls[7] = true; |
| nulls[8] = true; |
| 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] = true; |
| nulls[6] = true; |
| nulls[7] = true; |
| nulls[8] = true; |
| break; |
| case LOCKTAG_TRANSACTION: |
| values[5] = TransactionIdGetDatum(lock->tag.locktag_field1); |
| nulls[1] = true; |
| nulls[2] = true; |
| nulls[3] = true; |
| nulls[4] = true; |
| nulls[6] = true; |
| nulls[7] = true; |
| nulls[8] = true; |
| break; |
| case LOCKTAG_RELATION_APPENDONLY_SEGMENT_FILE: |
| values[1] = ObjectIdGetDatum(lock->tag.locktag_field1); |
| values[2] = ObjectIdGetDatum(lock->tag.locktag_field2); |
| values[7] = ObjectIdGetDatum(lock->tag.locktag_field3); |
| nulls[3] = true; |
| nulls[4] = true; |
| nulls[5] = true; |
| nulls[6] = true; |
| nulls[8] = true; |
| break; |
| case LOCKTAG_RESOURCE_QUEUE: |
| values[1] = ObjectIdGetDatum(proc->databaseId); |
| values[7] = ObjectIdGetDatum(lock->tag.locktag_field1); |
| nulls[2] = true; |
| nulls[3] = true; |
| nulls[4] = true; |
| nulls[5] = true; |
| nulls[6] = true; |
| nulls[8] = true; |
| break; |
| case LOCKTAG_OBJECT: |
| case LOCKTAG_USERLOCK: |
| case LOCKTAG_ADVISORY: |
| default: /* treat unknown locktags like OBJECT */ |
| values[1] = ObjectIdGetDatum(lock->tag.locktag_field1); |
| values[6] = ObjectIdGetDatum(lock->tag.locktag_field2); |
| values[7] = ObjectIdGetDatum(lock->tag.locktag_field3); |
| values[8] = Int16GetDatum(lock->tag.locktag_field4); |
| nulls[2] = true; |
| nulls[3] = true; |
| nulls[4] = true; |
| nulls[5] = true; |
| break; |
| } |
| |
| values[9] = TransactionIdGetDatum(proc->xid); |
| if (proc->pid != 0) |
| values[10] = Int32GetDatum(proc->pid); |
| else |
| nulls[10] = true; |
| values[11] = DirectFunctionCall1(textin, |
| CStringGetDatum((char *) GetLockmodeName(LOCK_LOCKMETHOD(*lock), |
| mode))); |
| values[12] = BoolGetDatum(granted); |
| |
| values[13] = Int32GetDatum(proc->mppSessionId); |
| |
| values[14] = Int32GetDatum(proc->mppIsWriter); |
| |
| values[15] = Int32GetDatum(GetQEIndex()); |
| |
| tuple = heap_form_tuple(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(); |
| } |