summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/adt/lockfuncs.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/utils/adt/lockfuncs.c
parentInitial commit. (diff)
downloadpostgresql-14-46651ce6fe013220ed397add242004d764fc0153.tar.xz
postgresql-14-46651ce6fe013220ed397add242004d764fc0153.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/utils/adt/lockfuncs.c')
-rw-r--r--src/backend/utils/adt/lockfuncs.c1069
1 files changed, 1069 insertions, 0 deletions
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
new file mode 100644
index 0000000..5dc0a58
--- /dev/null
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -0,0 +1,1069 @@
+/*-------------------------------------------------------------------------
+ *
+ * lockfuncs.c
+ * Functions for SQL access to various lock-manager capabilities.
+ *
+ * Copyright (c) 2002-2021, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/lockfuncs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "storage/predicate_internals.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+
+
+/*
+ * This must match enum LockTagType! Also, be sure to document any changes
+ * in the docs for the pg_locks view and for wait event types.
+ */
+const char *const LockTagTypeNames[] = {
+ "relation",
+ "extend",
+ "frozenid",
+ "page",
+ "tuple",
+ "transactionid",
+ "virtualxid",
+ "spectoken",
+ "object",
+ "userlock",
+ "advisory"
+};
+
+StaticAssertDecl(lengthof(LockTagTypeNames) == (LOCKTAG_ADVISORY + 1),
+ "array length mismatch");
+
+/* This must match enum PredicateLockTargetType (predicate_internals.h) */
+static const char *const PredicateLockTagTypeNames[] = {
+ "relation",
+ "page",
+ "tuple"
+};
+
+StaticAssertDecl(lengthof(PredicateLockTagTypeNames) == (PREDLOCKTAG_TUPLE + 1),
+ "array length mismatch");
+
+/* Working status for pg_lock_status */
+typedef struct
+{
+ LockData *lockData; /* state data from lmgr */
+ int currIdx; /* current PROCLOCK index */
+ PredicateLockData *predLockData; /* state data for pred locks */
+ int predLockIdx; /* current index for pred lock */
+} PG_Lock_Status;
+
+/* Number of columns in pg_locks output */
+#define NUM_LOCK_STATUS_COLUMNS 16
+
+/*
+ * VXIDGetDatum - Construct a text representation of a VXID
+ *
+ * This is currently only used in pg_lock_status, so we put it here.
+ */
+static Datum
+VXIDGetDatum(BackendId bid, LocalTransactionId lxid)
+{
+ /*
+ * The representation is "<bid>/<lxid>", decimal and unsigned decimal
+ * respectively. Note that elog.c also knows how to format a vxid.
+ */
+ char vxidstr[32];
+
+ snprintf(vxidstr, sizeof(vxidstr), "%d/%u", bid, lxid);
+
+ return CStringGetTextDatum(vxidstr);
+}
+
+
+/*
+ * 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;
+ PredicateLockData *predLockData;
+
+ 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 function's declaration in pg_proc.h */
+ tupdesc = CreateTemplateTupleDesc(NUM_LOCK_STATUS_COLUMNS);
+ 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, "virtualxid",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 7, "transactionid",
+ XIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 8, "classid",
+ OIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objid",
+ OIDOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 10, "objsubid",
+ INT2OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 11, "virtualtransaction",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 12, "pid",
+ INT4OID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 13, "mode",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 14, "granted",
+ BOOLOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 15, "fastpath",
+ BOOLOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 16, "waitstart",
+ TIMESTAMPTZOID, -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->predLockData = GetPredicateLockStatusData();
+ mystatus->predLockIdx = 0;
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ mystatus = (PG_Lock_Status *) funcctx->user_fctx;
+ lockData = mystatus->lockData;
+
+ while (mystatus->currIdx < lockData->nelements)
+ {
+ bool granted;
+ LOCKMODE mode = 0;
+ const char *locktypename;
+ char tnbuf[32];
+ Datum values[NUM_LOCK_STATUS_COLUMNS];
+ bool nulls[NUM_LOCK_STATUS_COLUMNS];
+ HeapTuple tuple;
+ Datum result;
+ LockInstanceData *instance;
+
+ instance = &(lockData->locks[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 (instance->holdMask)
+ {
+ for (mode = 0; mode < MAX_LOCKMODES; mode++)
+ {
+ if (instance->holdMask & LOCKBIT_ON(mode))
+ {
+ granted = true;
+ instance->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 (instance->waitLockMode != NoLock)
+ {
+ /* Yes, so report it with proper mode */
+ mode = instance->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 (instance->locktag.locktag_type <= LOCKTAG_LAST_TYPE)
+ locktypename = LockTagTypeNames[instance->locktag.locktag_type];
+ else
+ {
+ snprintf(tnbuf, sizeof(tnbuf), "unknown %d",
+ (int) instance->locktag.locktag_type);
+ locktypename = tnbuf;
+ }
+ values[0] = CStringGetTextDatum(locktypename);
+
+ switch ((LockTagType) instance->locktag.locktag_type)
+ {
+ case LOCKTAG_RELATION:
+ case LOCKTAG_RELATION_EXTEND:
+ values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
+ values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
+ nulls[3] = true;
+ nulls[4] = true;
+ nulls[5] = true;
+ nulls[6] = true;
+ nulls[7] = true;
+ nulls[8] = true;
+ nulls[9] = true;
+ break;
+ case LOCKTAG_DATABASE_FROZEN_IDS:
+ values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
+ nulls[2] = true;
+ nulls[3] = true;
+ nulls[4] = true;
+ nulls[5] = true;
+ nulls[6] = true;
+ nulls[7] = true;
+ nulls[8] = true;
+ nulls[9] = true;
+ break;
+ case LOCKTAG_PAGE:
+ values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
+ values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
+ values[3] = UInt32GetDatum(instance->locktag.locktag_field3);
+ nulls[4] = true;
+ nulls[5] = true;
+ nulls[6] = true;
+ nulls[7] = true;
+ nulls[8] = true;
+ nulls[9] = true;
+ break;
+ case LOCKTAG_TUPLE:
+ values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
+ values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
+ values[3] = UInt32GetDatum(instance->locktag.locktag_field3);
+ values[4] = UInt16GetDatum(instance->locktag.locktag_field4);
+ nulls[5] = true;
+ nulls[6] = true;
+ nulls[7] = true;
+ nulls[8] = true;
+ nulls[9] = true;
+ break;
+ case LOCKTAG_TRANSACTION:
+ values[6] =
+ TransactionIdGetDatum(instance->locktag.locktag_field1);
+ nulls[1] = true;
+ nulls[2] = true;
+ nulls[3] = true;
+ nulls[4] = true;
+ nulls[5] = true;
+ nulls[7] = true;
+ nulls[8] = true;
+ nulls[9] = true;
+ break;
+ case LOCKTAG_VIRTUALTRANSACTION:
+ values[5] = VXIDGetDatum(instance->locktag.locktag_field1,
+ instance->locktag.locktag_field2);
+ nulls[1] = true;
+ nulls[2] = true;
+ nulls[3] = true;
+ nulls[4] = true;
+ nulls[6] = true;
+ nulls[7] = true;
+ nulls[8] = true;
+ nulls[9] = true;
+ break;
+ case LOCKTAG_OBJECT:
+ case LOCKTAG_USERLOCK:
+ case LOCKTAG_ADVISORY:
+ default: /* treat unknown locktags like OBJECT */
+ values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
+ values[7] = ObjectIdGetDatum(instance->locktag.locktag_field2);
+ values[8] = ObjectIdGetDatum(instance->locktag.locktag_field3);
+ values[9] = Int16GetDatum(instance->locktag.locktag_field4);
+ nulls[2] = true;
+ nulls[3] = true;
+ nulls[4] = true;
+ nulls[5] = true;
+ nulls[6] = true;
+ break;
+ }
+
+ values[10] = VXIDGetDatum(instance->backend, instance->lxid);
+ if (instance->pid != 0)
+ values[11] = Int32GetDatum(instance->pid);
+ else
+ nulls[11] = true;
+ values[12] = CStringGetTextDatum(GetLockmodeName(instance->locktag.locktag_lockmethodid, mode));
+ values[13] = BoolGetDatum(granted);
+ values[14] = BoolGetDatum(instance->fastpath);
+ if (!granted && instance->waitStart != 0)
+ values[15] = TimestampTzGetDatum(instance->waitStart);
+ else
+ nulls[15] = true;
+
+ tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+ result = HeapTupleGetDatum(tuple);
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+
+ /*
+ * Have returned all regular locks. Now start on the SIREAD predicate
+ * locks.
+ */
+ predLockData = mystatus->predLockData;
+ if (mystatus->predLockIdx < predLockData->nelements)
+ {
+ PredicateLockTargetType lockType;
+
+ PREDICATELOCKTARGETTAG *predTag = &(predLockData->locktags[mystatus->predLockIdx]);
+ SERIALIZABLEXACT *xact = &(predLockData->xacts[mystatus->predLockIdx]);
+ Datum values[NUM_LOCK_STATUS_COLUMNS];
+ bool nulls[NUM_LOCK_STATUS_COLUMNS];
+ HeapTuple tuple;
+ Datum result;
+
+ mystatus->predLockIdx++;
+
+ /*
+ * Form tuple with appropriate data.
+ */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+
+ /* lock type */
+ lockType = GET_PREDICATELOCKTARGETTAG_TYPE(*predTag);
+
+ values[0] = CStringGetTextDatum(PredicateLockTagTypeNames[lockType]);
+
+ /* lock target */
+ values[1] = GET_PREDICATELOCKTARGETTAG_DB(*predTag);
+ values[2] = GET_PREDICATELOCKTARGETTAG_RELATION(*predTag);
+ if (lockType == PREDLOCKTAG_TUPLE)
+ values[4] = GET_PREDICATELOCKTARGETTAG_OFFSET(*predTag);
+ else
+ nulls[4] = true;
+ if ((lockType == PREDLOCKTAG_TUPLE) ||
+ (lockType == PREDLOCKTAG_PAGE))
+ values[3] = GET_PREDICATELOCKTARGETTAG_PAGE(*predTag);
+ else
+ nulls[3] = true;
+
+ /* these fields are targets for other types of locks */
+ nulls[5] = true; /* virtualxid */
+ nulls[6] = true; /* transactionid */
+ nulls[7] = true; /* classid */
+ nulls[8] = true; /* objid */
+ nulls[9] = true; /* objsubid */
+
+ /* lock holder */
+ values[10] = VXIDGetDatum(xact->vxid.backendId,
+ xact->vxid.localTransactionId);
+ if (xact->pid != 0)
+ values[11] = Int32GetDatum(xact->pid);
+ else
+ nulls[11] = true;
+
+ /*
+ * Lock mode. Currently all predicate locks are SIReadLocks, which are
+ * always held (never waiting) and have no fast path
+ */
+ values[12] = CStringGetTextDatum("SIReadLock");
+ values[13] = BoolGetDatum(true);
+ values[14] = BoolGetDatum(false);
+ nulls[15] = true;
+
+ tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+ result = HeapTupleGetDatum(tuple);
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+
+ SRF_RETURN_DONE(funcctx);
+}
+
+
+/*
+ * pg_blocking_pids - produce an array of the PIDs blocking given PID
+ *
+ * The reported PIDs are those that hold a lock conflicting with blocked_pid's
+ * current request (hard block), or are requesting such a lock and are ahead
+ * of blocked_pid in the lock's wait queue (soft block).
+ *
+ * In parallel-query cases, we report all PIDs blocking any member of the
+ * given PID's lock group, and the reported PIDs are those of the blocking
+ * PIDs' lock group leaders. This allows callers to compare the result to
+ * lists of clients' pg_backend_pid() results even during a parallel query.
+ *
+ * Parallel query makes it possible for there to be duplicate PIDs in the
+ * result (either because multiple waiters are blocked by same PID, or
+ * because multiple blockers have same group leader PID). We do not bother
+ * to eliminate such duplicates from the result.
+ *
+ * We need not consider predicate locks here, since those don't block anything.
+ */
+Datum
+pg_blocking_pids(PG_FUNCTION_ARGS)
+{
+ int blocked_pid = PG_GETARG_INT32(0);
+ Datum *arrayelems;
+ int narrayelems;
+ BlockedProcsData *lockData; /* state data from lmgr */
+ int i,
+ j;
+
+ /* Collect a snapshot of lock manager state */
+ lockData = GetBlockerStatusData(blocked_pid);
+
+ /* We can't need more output entries than there are reported PROCLOCKs */
+ arrayelems = (Datum *) palloc(lockData->nlocks * sizeof(Datum));
+ narrayelems = 0;
+
+ /* For each blocked proc in the lock group ... */
+ for (i = 0; i < lockData->nprocs; i++)
+ {
+ BlockedProcData *bproc = &lockData->procs[i];
+ LockInstanceData *instances = &lockData->locks[bproc->first_lock];
+ int *preceding_waiters = &lockData->waiter_pids[bproc->first_waiter];
+ LockInstanceData *blocked_instance;
+ LockMethod lockMethodTable;
+ int conflictMask;
+
+ /*
+ * Locate the blocked proc's own entry in the LockInstanceData array.
+ * There should be exactly one matching entry.
+ */
+ blocked_instance = NULL;
+ for (j = 0; j < bproc->num_locks; j++)
+ {
+ LockInstanceData *instance = &(instances[j]);
+
+ if (instance->pid == bproc->pid)
+ {
+ Assert(blocked_instance == NULL);
+ blocked_instance = instance;
+ }
+ }
+ Assert(blocked_instance != NULL);
+
+ lockMethodTable = GetLockTagsMethodTable(&(blocked_instance->locktag));
+ conflictMask = lockMethodTable->conflictTab[blocked_instance->waitLockMode];
+
+ /* Now scan the PROCLOCK data for conflicting procs */
+ for (j = 0; j < bproc->num_locks; j++)
+ {
+ LockInstanceData *instance = &(instances[j]);
+
+ /* A proc never blocks itself, so ignore that entry */
+ if (instance == blocked_instance)
+ continue;
+ /* Members of same lock group never block each other, either */
+ if (instance->leaderPid == blocked_instance->leaderPid)
+ continue;
+
+ if (conflictMask & instance->holdMask)
+ {
+ /* hard block: blocked by lock already held by this entry */
+ }
+ else if (instance->waitLockMode != NoLock &&
+ (conflictMask & LOCKBIT_ON(instance->waitLockMode)))
+ {
+ /* conflict in lock requests; who's in front in wait queue? */
+ bool ahead = false;
+ int k;
+
+ for (k = 0; k < bproc->num_waiters; k++)
+ {
+ if (preceding_waiters[k] == instance->pid)
+ {
+ /* soft block: this entry is ahead of blocked proc */
+ ahead = true;
+ break;
+ }
+ }
+ if (!ahead)
+ continue; /* not blocked by this entry */
+ }
+ else
+ {
+ /* not blocked by this entry */
+ continue;
+ }
+
+ /* blocked by this entry, so emit a record */
+ arrayelems[narrayelems++] = Int32GetDatum(instance->leaderPid);
+ }
+ }
+
+ /* Assert we didn't overrun arrayelems[] */
+ Assert(narrayelems <= lockData->nlocks);
+
+ /* Construct array, using hardwired knowledge about int4 type */
+ PG_RETURN_ARRAYTYPE_P(construct_array(arrayelems, narrayelems,
+ INT4OID,
+ sizeof(int32), true, TYPALIGN_INT));
+}
+
+
+/*
+ * pg_safe_snapshot_blocking_pids - produce an array of the PIDs blocking
+ * given PID from getting a safe snapshot
+ *
+ * XXX this does not consider parallel-query cases; not clear how big a
+ * problem that is in practice
+ */
+Datum
+pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
+{
+ int blocked_pid = PG_GETARG_INT32(0);
+ int *blockers;
+ int num_blockers;
+ Datum *blocker_datums;
+
+ /* A buffer big enough for any possible blocker list without truncation */
+ blockers = (int *) palloc(MaxBackends * sizeof(int));
+
+ /* Collect a snapshot of processes waited for by GetSafeSnapshot */
+ num_blockers =
+ GetSafeSnapshotBlockingPids(blocked_pid, blockers, MaxBackends);
+
+ /* Convert int array to Datum array */
+ if (num_blockers > 0)
+ {
+ int i;
+
+ blocker_datums = (Datum *) palloc(num_blockers * sizeof(Datum));
+ for (i = 0; i < num_blockers; ++i)
+ blocker_datums[i] = Int32GetDatum(blockers[i]);
+ }
+ else
+ blocker_datums = NULL;
+
+ /* Construct array, using hardwired knowledge about int4 type */
+ PG_RETURN_ARRAYTYPE_P(construct_array(blocker_datums, num_blockers,
+ INT4OID,
+ sizeof(int32), true, TYPALIGN_INT));
+}
+
+
+/*
+ * pg_isolation_test_session_is_blocked - support function for isolationtester
+ *
+ * Check if specified PID is blocked by any of the PIDs listed in the second
+ * argument. Currently, this looks for blocking caused by waiting for
+ * heavyweight locks or safe snapshots. We ignore blockage caused by PIDs
+ * not directly under the isolationtester's control, eg autovacuum.
+ *
+ * This is an undocumented function intended for use by the isolation tester,
+ * and may change in future releases as required for testing purposes.
+ */
+Datum
+pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
+{
+ int blocked_pid = PG_GETARG_INT32(0);
+ ArrayType *interesting_pids_a = PG_GETARG_ARRAYTYPE_P(1);
+ ArrayType *blocking_pids_a;
+ int32 *interesting_pids;
+ int32 *blocking_pids;
+ int num_interesting_pids;
+ int num_blocking_pids;
+ int dummy;
+ int i,
+ j;
+
+ /* Validate the passed-in array */
+ Assert(ARR_ELEMTYPE(interesting_pids_a) == INT4OID);
+ if (array_contains_nulls(interesting_pids_a))
+ elog(ERROR, "array must not contain nulls");
+ interesting_pids = (int32 *) ARR_DATA_PTR(interesting_pids_a);
+ num_interesting_pids = ArrayGetNItems(ARR_NDIM(interesting_pids_a),
+ ARR_DIMS(interesting_pids_a));
+
+ /*
+ * Get the PIDs of all sessions blocking the given session's attempt to
+ * acquire heavyweight locks.
+ */
+ blocking_pids_a =
+ DatumGetArrayTypeP(DirectFunctionCall1(pg_blocking_pids, blocked_pid));
+
+ Assert(ARR_ELEMTYPE(blocking_pids_a) == INT4OID);
+ Assert(!array_contains_nulls(blocking_pids_a));
+ blocking_pids = (int32 *) ARR_DATA_PTR(blocking_pids_a);
+ num_blocking_pids = ArrayGetNItems(ARR_NDIM(blocking_pids_a),
+ ARR_DIMS(blocking_pids_a));
+
+ /*
+ * Check if any of these are in the list of interesting PIDs, that being
+ * the sessions that the isolation tester is running. We don't use
+ * "arrayoverlaps" here, because it would lead to cache lookups and one of
+ * our goals is to run quickly with debug_discard_caches > 0. We expect
+ * blocking_pids to be usually empty and otherwise a very small number in
+ * isolation tester cases, so make that the outer loop of a naive search
+ * for a match.
+ */
+ for (i = 0; i < num_blocking_pids; i++)
+ for (j = 0; j < num_interesting_pids; j++)
+ {
+ if (blocking_pids[i] == interesting_pids[j])
+ PG_RETURN_BOOL(true);
+ }
+
+ /*
+ * Check if blocked_pid is waiting for a safe snapshot. We could in
+ * theory check the resulting array of blocker PIDs against the
+ * interesting PIDs list, but since there is no danger of autovacuum
+ * blocking GetSafeSnapshot there seems to be no point in expending cycles
+ * on allocating a buffer and searching for overlap; so it's presently
+ * sufficient for the isolation tester's purposes to use a single element
+ * buffer and check if the number of safe snapshot blockers is non-zero.
+ */
+ if (GetSafeSnapshotBlockingPids(blocked_pid, &dummy, 1) > 0)
+ PG_RETURN_BOOL(true);
+
+ PG_RETURN_BOOL(false);
+}
+
+
+/*
+ * 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_xact_lock(int8) - acquire xact scoped
+ * exclusive lock on an int8 key
+ */
+Datum
+pg_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
+{
+ int64 key = PG_GETARG_INT64(0);
+ LOCKTAG tag;
+
+ SET_LOCKTAG_INT64(tag, key);
+
+ (void) LockAcquire(&tag, ExclusiveLock, false, 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_advisory_xact_lock_shared(int8) - acquire xact scoped
+ * share lock on an int8 key
+ */
+Datum
+pg_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
+{
+ int64 key = PG_GETARG_INT64(0);
+ LOCKTAG tag;
+
+ SET_LOCKTAG_INT64(tag, key);
+
+ (void) LockAcquire(&tag, ShareLock, false, 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_xact_lock(int8) - acquire xact scoped
+ * exclusive lock on an int8 key, no wait
+ *
+ * Returns true if successful, false if lock not available
+ */
+Datum
+pg_try_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
+{
+ int64 key = PG_GETARG_INT64(0);
+ LOCKTAG tag;
+ LockAcquireResult res;
+
+ SET_LOCKTAG_INT64(tag, key);
+
+ res = LockAcquire(&tag, ExclusiveLock, false, 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_try_advisory_xact_lock_shared(int8) - acquire xact scoped
+ * share lock on an int8 key, no wait
+ *
+ * Returns true if successful, false if lock not available
+ */
+Datum
+pg_try_advisory_xact_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, false, 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_xact_lock(int4, int4) - acquire xact scoped
+ * exclusive lock on 2 int4 keys
+ */
+Datum
+pg_advisory_xact_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, false, 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_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
+ * share lock on 2 int4 keys
+ */
+Datum
+pg_advisory_xact_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, false, 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_xact_lock(int4, int4) - acquire xact scoped
+ * exclusive lock on 2 int4 keys, no wait
+ *
+ * Returns true if successful, false if lock not available
+ */
+Datum
+pg_try_advisory_xact_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, false, 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_try_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
+ * share lock on 2 int4 keys, no wait
+ *
+ * Returns true if successful, false if lock not available
+ */
+Datum
+pg_try_advisory_xact_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, false, 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)
+{
+ LockReleaseSession(USER_LOCKMETHOD);
+
+ PG_RETURN_VOID();
+}