1/*-------------------------------------------------------------------------
4 * Functions for SQL access to various lock-manager capabilities.
6 * Copyright (c) 2002-2025, PostgreSQL Global Development Group
9 * src/backend/utils/adt/lockfuncs.c
11 *-------------------------------------------------------------------------
24 * This must match enum LockTagType! Also, be sure to document any changes
25 * in the docs for the pg_locks view and update the WaitEventLOCK section in
26 * src/backend/utils/activity/wait_event_names.txt.
44 "array length mismatch");
46/* This must match enum PredicateLockTargetType (predicate_internals.h) */
54 "array length mismatch");
56/* Working status for pg_lock_status */
65/* Number of columns in pg_locks output */
66 #define NUM_LOCK_STATUS_COLUMNS 16
69 * VXIDGetDatum - Construct a text representation of a VXID
71 * This is currently only used in pg_lock_status, so we put it here.
77 * The representation is "<procNumber>/<lxid>", decimal and unsigned
78 * decimal respectively. Note that elog.c also knows how to format a
83 snprintf(vxidstr,
sizeof(vxidstr),
"%d/%u", procNumber, lxid);
90 * pg_lock_status - produce a view with one row per held or awaited lock mode
105 /* create a function context for cross-call persistence */
109 * switch to memory context appropriate for multiple function calls
113 /* build tupdesc for result tuples */
114 /* this had better match function's declaration in pg_proc.h */
147 TIMESTAMPTZOID, -1, 0);
152 * Collect all the locking information that we will format and send
153 * out as a result set.
174 const char *locktypename;
185 * Look to see if there are any held lock modes in this PROCLOCK. If
186 * so, report, and destructively modify lockData so we don't report
204 * If no (more) held modes to report, see if PROC is waiting for a
211 /* Yes, so report it with proper mode */
215 * We are now done with this PROCLOCK, so advance pointer to
216 * continue with next one on next call.
223 * Okay, we've displayed all the locks associated with this
224 * PROCLOCK, proceed to the next one.
232 * Form tuple with appropriate data.
239 snprintf(tnbuf,
sizeof(tnbuf),
"unknown %d",
241 locktypename = tnbuf;
342 default:
/* treat unknown locktags like OBJECT */
356 if (instance->
pid != 0)
363 if (!granted && instance->
waitStart != 0)
374 * Have returned all regular locks. Now start on the SIREAD predicate
392 * Form tuple with appropriate data.
413 /* these fields are targets for other types of locks */
414 nulls[5] =
true;
/* virtualxid */
415 nulls[6] =
true;
/* transactionid */
416 nulls[7] =
true;
/* classid */
417 nulls[8] =
true;
/* objid */
418 nulls[9] =
true;
/* objsubid */
429 * Lock mode. Currently all predicate locks are SIReadLocks, which are
430 * always held (never waiting) and have no fast path
447 * pg_blocking_pids - produce an array of the PIDs blocking given PID
449 * The reported PIDs are those that hold a lock conflicting with blocked_pid's
450 * current request (hard block), or are requesting such a lock and are ahead
451 * of blocked_pid in the lock's wait queue (soft block).
453 * In parallel-query cases, we report all PIDs blocking any member of the
454 * given PID's lock group, and the reported PIDs are those of the blocking
455 * PIDs' lock group leaders. This allows callers to compare the result to
456 * lists of clients' pg_backend_pid() results even during a parallel query.
458 * Parallel query makes it possible for there to be duplicate PIDs in the
459 * result (either because multiple waiters are blocked by same PID, or
460 * because multiple blockers have same group leader PID). We do not bother
461 * to eliminate such duplicates from the result.
463 * We need not consider predicate locks here, since those don't block anything.
475 /* Collect a snapshot of lock manager state */
478 /* We can't need more output entries than there are reported PROCLOCKs */
482 /* For each blocked proc in the lock group ... */
493 * Locate the blocked proc's own entry in the LockInstanceData array.
494 * There should be exactly one matching entry.
496 blocked_instance = NULL;
501 if (instance->
pid == bproc->
pid)
503 Assert(blocked_instance == NULL);
504 blocked_instance = instance;
507 Assert(blocked_instance != NULL);
512 /* Now scan the PROCLOCK data for conflicting procs */
517 /* A proc never blocks itself, so ignore that entry */
518 if (instance == blocked_instance)
520 /* Members of same lock group never block each other, either */
524 if (conflictMask & instance->
holdMask)
526 /* hard block: blocked by lock already held by this entry */
531 /* conflict in lock requests; who's in front in wait queue? */
537 if (preceding_waiters[k] == instance->
pid)
539 /* soft block: this entry is ahead of blocked proc */
545 continue;
/* not blocked by this entry */
549 /* not blocked by this entry */
553 /* blocked by this entry, so emit a record */
558 /* Assert we didn't overrun arrayelems[] */
559 Assert(narrayelems <= lockData->nlocks);
566 * pg_safe_snapshot_blocking_pids - produce an array of the PIDs blocking
567 * given PID from getting a safe snapshot
569 * XXX this does not consider parallel-query cases; not clear how big a
570 * problem that is in practice
578 Datum *blocker_datums;
580 /* A buffer big enough for any possible blocker list without truncation */
583 /* Collect a snapshot of processes waited for by GetSafeSnapshot */
587 /* Convert int array to Datum array */
588 if (num_blockers > 0)
593 for (
i = 0;
i < num_blockers; ++
i)
597 blocker_datums = NULL;
604 * Functions for manipulating advisory locks
606 * We make use of the locktag fields as follows:
608 * field1: MyDatabaseId ... ensures locks are local to each database
609 * field2: first of 2 int4 keys, or high-order half of an int8 key
610 * field3: second of 2 int4 keys, or low-order half of an int8 key
611 * field4: 1 if using an int8 key, 2 if using 2 int4 keys
613 #define SET_LOCKTAG_INT64(tag, key64) \
614 SET_LOCKTAG_ADVISORY(tag, \
616 (uint32) ((key64) >> 32), \
619 #define SET_LOCKTAG_INT32(tag, key1, key2) \
620 SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, key1, key2, 2)
623 * pg_advisory_lock(int8) - acquire exclusive lock on an int8 key
639 * pg_advisory_xact_lock(int8) - acquire xact scoped
640 * exclusive lock on an int8 key
656 * pg_advisory_lock_shared(int8) - acquire share lock on an int8 key
672 * pg_advisory_xact_lock_shared(int8) - acquire xact scoped
673 * share lock on an int8 key
689 * pg_try_advisory_lock(int8) - acquire exclusive lock on an int8 key, no wait
691 * Returns true if successful, false if lock not available
708 * pg_try_advisory_xact_lock(int8) - acquire xact scoped
709 * exclusive lock on an int8 key, no wait
711 * Returns true if successful, false if lock not available
728 * pg_try_advisory_lock_shared(int8) - acquire share lock on an int8 key, no wait
730 * Returns true if successful, false if lock not available
747 * pg_try_advisory_xact_lock_shared(int8) - acquire xact scoped
748 * share lock on an int8 key, no wait
750 * Returns true if successful, false if lock not available
767 * pg_advisory_unlock(int8) - release exclusive lock on an int8 key
769 * Returns true if successful, false if lock was not held
786 * pg_advisory_unlock_shared(int8) - release share lock on an int8 key
788 * Returns true if successful, false if lock was not held
805 * pg_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys
822 * pg_advisory_xact_lock(int4, int4) - acquire xact scoped
823 * exclusive lock on 2 int4 keys
840 * pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys
857 * pg_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
858 * share lock on 2 int4 keys
875 * pg_try_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys, no wait
877 * Returns true if successful, false if lock not available
895 * pg_try_advisory_xact_lock(int4, int4) - acquire xact scoped
896 * exclusive lock on 2 int4 keys, no wait
898 * Returns true if successful, false if lock not available
916 * pg_try_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys, no wait
918 * Returns true if successful, false if lock not available
936 * pg_try_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
937 * share lock on 2 int4 keys, no wait
939 * Returns true if successful, false if lock not available
957 * pg_advisory_unlock(int4, int4) - release exclusive lock on 2 int4 keys
959 * Returns true if successful, false if lock was not held
977 * pg_advisory_unlock_shared(int4, int4) - release share lock on 2 int4 keys
979 * Returns true if successful, false if lock was not held
997 * pg_advisory_unlock_all() - release all advisory locks
#define PG_RETURN_ARRAYTYPE_P(x)
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
static Datum values[MAXATTR]
#define CStringGetTextDatum(s)
uint32 LocalTransactionId
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
#define PG_GETARG_INT64(n)
#define PG_GETARG_INT32(n)
#define PG_RETURN_BOOL(x)
#define SRF_IS_FIRSTCALL()
#define SRF_PERCALL_SETUP()
#define SRF_RETURN_NEXT(_funcctx, _result)
#define SRF_FIRSTCALL_INIT()
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
#define SRF_RETURN_DONE(_funcctx)
Assert(PointerIsAligned(start, uint64))
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
LockAcquireResult LockAcquire(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock, bool dontWait)
bool LockRelease(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock)
BlockedProcsData * GetBlockerStatusData(int blocked_pid)
const char * GetLockmodeName(LOCKMETHODID lockmethodid, LOCKMODE mode)
void LockReleaseSession(LOCKMETHODID lockmethodid)
LockData * GetLockStatusData(void)
LockMethod GetLockTagsMethodTable(const LOCKTAG *locktag)
@ LOCKTAG_RELATION_EXTEND
@ LOCKTAG_SPECULATIVE_TOKEN
@ LOCKTAG_APPLY_TRANSACTION
@ LOCKTAG_DATABASE_FROZEN_IDS
@ LOCKTAG_VIRTUALTRANSACTION
#define LOCKBIT_OFF(lockmode)
#define LOCKTAG_LAST_TYPE
#define LOCKBIT_ON(lockmode)
Datum pg_advisory_lock_int4(PG_FUNCTION_ARGS)
#define SET_LOCKTAG_INT64(tag, key64)
Datum pg_try_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
Datum pg_advisory_unlock_int8(PG_FUNCTION_ARGS)
Datum pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
Datum pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
Datum pg_advisory_lock_int8(PG_FUNCTION_ARGS)
Datum pg_try_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
Datum pg_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
Datum pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
static Datum VXIDGetDatum(ProcNumber procNumber, LocalTransactionId lxid)
Datum pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
#define SET_LOCKTAG_INT32(tag, key1, key2)
#define NUM_LOCK_STATUS_COLUMNS
Datum pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
Datum pg_advisory_unlock_int4(PG_FUNCTION_ARGS)
static const char *const PredicateLockTagTypeNames[]
Datum pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
Datum pg_lock_status(PG_FUNCTION_ARGS)
Datum pg_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
StaticAssertDecl(lengthof(LockTagTypeNames)==(LOCKTAG_LAST_TYPE+1), "array length mismatch")
Datum pg_try_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
Datum pg_blocking_pids(PG_FUNCTION_ARGS)
Datum pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
Datum pg_try_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
Datum pg_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
Datum pg_advisory_unlock_shared_int8(PG_FUNCTION_ARGS)
Datum pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
Datum pg_advisory_unlock_all(PG_FUNCTION_ARGS)
const char *const LockTagTypeNames[]
Datum pg_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
static PgChecksumMode mode
static Datum TransactionIdGetDatum(TransactionId X)
static Datum Int16GetDatum(int16 X)
static Datum UInt16GetDatum(uint16 X)
static Datum BoolGetDatum(bool X)
static Datum ObjectIdGetDatum(Oid X)
static Datum Int32GetDatum(int32 X)
static Datum UInt32GetDatum(uint32 X)
PredicateLockData * GetPredicateLockStatusData(void)
int GetSafeSnapshotBlockingPids(int blocked_pid, int *output, int output_size)
#define GET_PREDICATELOCKTARGETTAG_DB(locktag)
#define GET_PREDICATELOCKTARGETTAG_RELATION(locktag)
#define GET_PREDICATELOCKTARGETTAG_TYPE(locktag)
#define GET_PREDICATELOCKTARGETTAG_PAGE(locktag)
#define GET_PREDICATELOCKTARGETTAG_OFFSET(locktag)
MemoryContext multi_call_memory_ctx
uint8 locktag_lockmethodid
VirtualTransactionId vxid
const LOCKMASK * conflictTab
PredicateLockData * predLockData
PREDICATELOCKTARGETTAG * locktags
VirtualTransactionId vxid
LocalTransactionId localTransactionId
TupleDesc CreateTemplateTupleDesc(int natts)
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
static Datum TimestampTzGetDatum(TimestampTz X)