1/*-------------------------------------------------------------------------
2 *
3 * lockfuncs.c
4 * Functions for SQL access to various lock-manager capabilities.
5 *
6 * Copyright (c) 2002-2019, PostgreSQL Global Development Group
7 *
8 * IDENTIFICATION
9 * src/backend/utils/adt/lockfuncs.c
10 *
11 *-------------------------------------------------------------------------
12 */
13#include "postgres.h"
14
15#include "access/htup_details.h"
16#include "access/xact.h"
17#include "catalog/pg_type.h"
18#include "funcapi.h"
19#include "miscadmin.h"
20#include "storage/predicate_internals.h"
21#include "utils/array.h"
22#include "utils/builtins.h"
23
24
25/* This must match enum LockTagType! */
26const char *const LockTagTypeNames[] = {
27 "relation",
28 "extend",
29 "page",
30 "tuple",
31 "transactionid",
32 "virtualxid",
33 "speculative token",
34 "object",
35 "userlock",
36 "advisory"
37};
38
39/* This must match enum PredicateLockTargetType (predicate_internals.h) */
40static const char *const PredicateLockTagTypeNames[] = {
41 "relation",
42 "page",
43 "tuple"
44};
45
46/* Working status for pg_lock_status */
47typedef struct
48{
49 LockData *lockData; /* state data from lmgr */
50 int currIdx; /* current PROCLOCK index */
51 PredicateLockData *predLockData; /* state data for pred locks */
52 int predLockIdx; /* current index for pred lock */
53} PG_Lock_Status;
54
55/* Number of columns in pg_locks output */
56#define NUM_LOCK_STATUS_COLUMNS 15
57
58/*
59 * VXIDGetDatum - Construct a text representation of a VXID
60 *
61 * This is currently only used in pg_lock_status, so we put it here.
62 */
63static Datum
64VXIDGetDatum(BackendId bid, LocalTransactionId lxid)
65{
66 /*
67 * The representation is "<bid>/<lxid>", decimal and unsigned decimal
68 * respectively. Note that elog.c also knows how to format a vxid.
69 */
70 char vxidstr[32];
71
72 snprintf(vxidstr, sizeof(vxidstr), "%d/%u", bid, lxid);
73
74 return CStringGetTextDatum(vxidstr);
75}
76
77
78/*
79 * pg_lock_status - produce a view with one row per held or awaited lock mode
80 */
81Datum
82pg_lock_status(PG_FUNCTION_ARGS)
83{
84 FuncCallContext *funcctx;
85 PG_Lock_Status *mystatus;
86 LockData *lockData;
87 PredicateLockData *predLockData;
88
89 if (SRF_IS_FIRSTCALL())
90 {
91 TupleDesc tupdesc;
92 MemoryContext oldcontext;
93
94 /* create a function context for cross-call persistence */
95 funcctx = SRF_FIRSTCALL_INIT();
96
97 /*
98 * switch to memory context appropriate for multiple function calls
99 */
100 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
101
102 /* build tupdesc for result tuples */
103 /* this had better match function's declaration in pg_proc.h */
104 tupdesc = CreateTemplateTupleDesc(NUM_LOCK_STATUS_COLUMNS);
105 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype",
106 TEXTOID, -1, 0);
107 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
108 OIDOID, -1, 0);
109 TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relation",
110 OIDOID, -1, 0);
111 TupleDescInitEntry(tupdesc, (AttrNumber) 4, "page",
112 INT4OID, -1, 0);
113 TupleDescInitEntry(tupdesc, (AttrNumber) 5, "tuple",
114 INT2OID, -1, 0);
115 TupleDescInitEntry(tupdesc, (AttrNumber) 6, "virtualxid",
116 TEXTOID, -1, 0);
117 TupleDescInitEntry(tupdesc, (AttrNumber) 7, "transactionid",
118 XIDOID, -1, 0);
119 TupleDescInitEntry(tupdesc, (AttrNumber) 8, "classid",
120 OIDOID, -1, 0);
121 TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objid",
122 OIDOID, -1, 0);
123 TupleDescInitEntry(tupdesc, (AttrNumber) 10, "objsubid",
124 INT2OID, -1, 0);
125 TupleDescInitEntry(tupdesc, (AttrNumber) 11, "virtualtransaction",
126 TEXTOID, -1, 0);
127 TupleDescInitEntry(tupdesc, (AttrNumber) 12, "pid",
128 INT4OID, -1, 0);
129 TupleDescInitEntry(tupdesc, (AttrNumber) 13, "mode",
130 TEXTOID, -1, 0);
131 TupleDescInitEntry(tupdesc, (AttrNumber) 14, "granted",
132 BOOLOID, -1, 0);
133 TupleDescInitEntry(tupdesc, (AttrNumber) 15, "fastpath",
134 BOOLOID, -1, 0);
135
136 funcctx->tuple_desc = BlessTupleDesc(tupdesc);
137
138 /*
139 * Collect all the locking information that we will format and send
140 * out as a result set.
141 */
142 mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status));
143 funcctx->user_fctx = (void *) mystatus;
144
145 mystatus->lockData = GetLockStatusData();
146 mystatus->currIdx = 0;
147 mystatus->predLockData = GetPredicateLockStatusData();
148 mystatus->predLockIdx = 0;
149
150 MemoryContextSwitchTo(oldcontext);
151 }
152
153 funcctx = SRF_PERCALL_SETUP();
154 mystatus = (PG_Lock_Status *) funcctx->user_fctx;
155 lockData = mystatus->lockData;
156
157 while (mystatus->currIdx < lockData->nelements)
158 {
159 bool granted;
160 LOCKMODE mode = 0;
161 const char *locktypename;
162 char tnbuf[32];
163 Datum values[NUM_LOCK_STATUS_COLUMNS];
164 bool nulls[NUM_LOCK_STATUS_COLUMNS];
165 HeapTuple tuple;
166 Datum result;
167 LockInstanceData *instance;
168
169 instance = &(lockData->locks[mystatus->currIdx]);
170
171 /*
172 * Look to see if there are any held lock modes in this PROCLOCK. If
173 * so, report, and destructively modify lockData so we don't report
174 * again.
175 */
176 granted = false;
177 if (instance->holdMask)
178 {
179 for (mode = 0; mode < MAX_LOCKMODES; mode++)
180 {
181 if (instance->holdMask & LOCKBIT_ON(mode))
182 {
183 granted = true;
184 instance->holdMask &= LOCKBIT_OFF(mode);
185 break;
186 }
187 }
188 }
189
190 /*
191 * If no (more) held modes to report, see if PROC is waiting for a
192 * lock on this lock.
193 */
194 if (!granted)
195 {
196 if (instance->waitLockMode != NoLock)
197 {
198 /* Yes, so report it with proper mode */
199 mode = instance->waitLockMode;
200
201 /*
202 * We are now done with this PROCLOCK, so advance pointer to
203 * continue with next one on next call.
204 */
205 mystatus->currIdx++;
206 }
207 else
208 {
209 /*
210 * Okay, we've displayed all the locks associated with this
211 * PROCLOCK, proceed to the next one.
212 */
213 mystatus->currIdx++;
214 continue;
215 }
216 }
217
218 /*
219 * Form tuple with appropriate data.
220 */
221 MemSet(values, 0, sizeof(values));
222 MemSet(nulls, false, sizeof(nulls));
223
224 if (instance->locktag.locktag_type <= LOCKTAG_LAST_TYPE)
225 locktypename = LockTagTypeNames[instance->locktag.locktag_type];
226 else
227 {
228 snprintf(tnbuf, sizeof(tnbuf), "unknown %d",
229 (int) instance->locktag.locktag_type);
230 locktypename = tnbuf;
231 }
232 values[0] = CStringGetTextDatum(locktypename);
233
234 switch ((LockTagType) instance->locktag.locktag_type)
235 {
236 case LOCKTAG_RELATION:
237 case LOCKTAG_RELATION_EXTEND:
238 values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
239 values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
240 nulls[3] = true;
241 nulls[4] = true;
242 nulls[5] = true;
243 nulls[6] = true;
244 nulls[7] = true;
245 nulls[8] = true;
246 nulls[9] = true;
247 break;
248 case LOCKTAG_PAGE:
249 values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
250 values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
251 values[3] = UInt32GetDatum(instance->locktag.locktag_field3);
252 nulls[4] = true;
253 nulls[5] = true;
254 nulls[6] = true;
255 nulls[7] = true;
256 nulls[8] = true;
257 nulls[9] = true;
258 break;
259 case LOCKTAG_TUPLE:
260 values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
261 values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
262 values[3] = UInt32GetDatum(instance->locktag.locktag_field3);
263 values[4] = UInt16GetDatum(instance->locktag.locktag_field4);
264 nulls[5] = true;
265 nulls[6] = true;
266 nulls[7] = true;
267 nulls[8] = true;
268 nulls[9] = true;
269 break;
270 case LOCKTAG_TRANSACTION:
271 values[6] =
272 TransactionIdGetDatum(instance->locktag.locktag_field1);
273 nulls[1] = true;
274 nulls[2] = true;
275 nulls[3] = true;
276 nulls[4] = true;
277 nulls[5] = true;
278 nulls[7] = true;
279 nulls[8] = true;
280 nulls[9] = true;
281 break;
282 case LOCKTAG_VIRTUALTRANSACTION:
283 values[5] = VXIDGetDatum(instance->locktag.locktag_field1,
284 instance->locktag.locktag_field2);
285 nulls[1] = true;
286 nulls[2] = true;
287 nulls[3] = true;
288 nulls[4] = true;
289 nulls[6] = true;
290 nulls[7] = true;
291 nulls[8] = true;
292 nulls[9] = true;
293 break;
294 case LOCKTAG_OBJECT:
295 case LOCKTAG_USERLOCK:
296 case LOCKTAG_ADVISORY:
297 default: /* treat unknown locktags like OBJECT */
298 values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
299 values[7] = ObjectIdGetDatum(instance->locktag.locktag_field2);
300 values[8] = ObjectIdGetDatum(instance->locktag.locktag_field3);
301 values[9] = Int16GetDatum(instance->locktag.locktag_field4);
302 nulls[2] = true;
303 nulls[3] = true;
304 nulls[4] = true;
305 nulls[5] = true;
306 nulls[6] = true;
307 break;
308 }
309
310 values[10] = VXIDGetDatum(instance->backend, instance->lxid);
311 if (instance->pid != 0)
312 values[11] = Int32GetDatum(instance->pid);
313 else
314 nulls[11] = true;
315 values[12] = CStringGetTextDatum(GetLockmodeName(instance->locktag.locktag_lockmethodid, mode));
316 values[13] = BoolGetDatum(granted);
317 values[14] = BoolGetDatum(instance->fastpath);
318
319 tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
320 result = HeapTupleGetDatum(tuple);
321 SRF_RETURN_NEXT(funcctx, result);
322 }
323
324 /*
325 * Have returned all regular locks. Now start on the SIREAD predicate
326 * locks.
327 */
328 predLockData = mystatus->predLockData;
329 if (mystatus->predLockIdx < predLockData->nelements)
330 {
331 PredicateLockTargetType lockType;
332
333 PREDICATELOCKTARGETTAG *predTag = &(predLockData->locktags[mystatus->predLockIdx]);
334 SERIALIZABLEXACT *xact = &(predLockData->xacts[mystatus->predLockIdx]);
335 Datum values[NUM_LOCK_STATUS_COLUMNS];
336 bool nulls[NUM_LOCK_STATUS_COLUMNS];
337 HeapTuple tuple;
338 Datum result;
339
340 mystatus->predLockIdx++;
341
342 /*
343 * Form tuple with appropriate data.
344 */
345 MemSet(values, 0, sizeof(values));
346 MemSet(nulls, false, sizeof(nulls));
347
348 /* lock type */
349 lockType = GET_PREDICATELOCKTARGETTAG_TYPE(*predTag);
350
351 values[0] = CStringGetTextDatum(PredicateLockTagTypeNames[lockType]);
352
353 /* lock target */
354 values[1] = GET_PREDICATELOCKTARGETTAG_DB(*predTag);
355 values[2] = GET_PREDICATELOCKTARGETTAG_RELATION(*predTag);
356 if (lockType == PREDLOCKTAG_TUPLE)
357 values[4] = GET_PREDICATELOCKTARGETTAG_OFFSET(*predTag);
358 else
359 nulls[4] = true;
360 if ((lockType == PREDLOCKTAG_TUPLE) ||
361 (lockType == PREDLOCKTAG_PAGE))
362 values[3] = GET_PREDICATELOCKTARGETTAG_PAGE(*predTag);
363 else
364 nulls[3] = true;
365
366 /* these fields are targets for other types of locks */
367 nulls[5] = true; /* virtualxid */
368 nulls[6] = true; /* transactionid */
369 nulls[7] = true; /* classid */
370 nulls[8] = true; /* objid */
371 nulls[9] = true; /* objsubid */
372
373 /* lock holder */
374 values[10] = VXIDGetDatum(xact->vxid.backendId,
375 xact->vxid.localTransactionId);
376 if (xact->pid != 0)
377 values[11] = Int32GetDatum(xact->pid);
378 else
379 nulls[11] = true;
380
381 /*
382 * Lock mode. Currently all predicate locks are SIReadLocks, which are
383 * always held (never waiting) and have no fast path
384 */
385 values[12] = CStringGetTextDatum("SIReadLock");
386 values[13] = BoolGetDatum(true);
387 values[14] = BoolGetDatum(false);
388
389 tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
390 result = HeapTupleGetDatum(tuple);
391 SRF_RETURN_NEXT(funcctx, result);
392 }
393
394 SRF_RETURN_DONE(funcctx);
395}
396
397
398/*
399 * pg_blocking_pids - produce an array of the PIDs blocking given PID
400 *
401 * The reported PIDs are those that hold a lock conflicting with blocked_pid's
402 * current request (hard block), or are requesting such a lock and are ahead
403 * of blocked_pid in the lock's wait queue (soft block).
404 *
405 * In parallel-query cases, we report all PIDs blocking any member of the
406 * given PID's lock group, and the reported PIDs are those of the blocking
407 * PIDs' lock group leaders. This allows callers to compare the result to
408 * lists of clients' pg_backend_pid() results even during a parallel query.
409 *
410 * Parallel query makes it possible for there to be duplicate PIDs in the
411 * result (either because multiple waiters are blocked by same PID, or
412 * because multiple blockers have same group leader PID). We do not bother
413 * to eliminate such duplicates from the result.
414 *
415 * We need not consider predicate locks here, since those don't block anything.
416 */
417Datum
418pg_blocking_pids(PG_FUNCTION_ARGS)
419{
420 int blocked_pid = PG_GETARG_INT32(0);
421 Datum *arrayelems;
422 int narrayelems;
423 BlockedProcsData *lockData; /* state data from lmgr */
424 int i,
425 j;
426
427 /* Collect a snapshot of lock manager state */
428 lockData = GetBlockerStatusData(blocked_pid);
429
430 /* We can't need more output entries than there are reported PROCLOCKs */
431 arrayelems = (Datum *) palloc(lockData->nlocks * sizeof(Datum));
432 narrayelems = 0;
433
434 /* For each blocked proc in the lock group ... */
435 for (i = 0; i < lockData->nprocs; i++)
436 {
437 BlockedProcData *bproc = &lockData->procs[i];
438 LockInstanceData *instances = &lockData->locks[bproc->first_lock];
439 int *preceding_waiters = &lockData->waiter_pids[bproc->first_waiter];
440 LockInstanceData *blocked_instance;
441 LockMethod lockMethodTable;
442 int conflictMask;
443
444 /*
445 * Locate the blocked proc's own entry in the LockInstanceData array.
446 * There should be exactly one matching entry.
447 */
448 blocked_instance = NULL;
449 for (j = 0; j < bproc->num_locks; j++)
450 {
451 LockInstanceData *instance = &(instances[j]);
452
453 if (instance->pid == bproc->pid)
454 {
455 Assert(blocked_instance == NULL);
456 blocked_instance = instance;
457 }
458 }
459 Assert(blocked_instance != NULL);
460
461 lockMethodTable = GetLockTagsMethodTable(&(blocked_instance->locktag));
462 conflictMask = lockMethodTable->conflictTab[blocked_instance->waitLockMode];
463
464 /* Now scan the PROCLOCK data for conflicting procs */
465 for (j = 0; j < bproc->num_locks; j++)
466 {
467 LockInstanceData *instance = &(instances[j]);
468
469 /* A proc never blocks itself, so ignore that entry */
470 if (instance == blocked_instance)
471 continue;
472 /* Members of same lock group never block each other, either */
473 if (instance->leaderPid == blocked_instance->leaderPid)
474 continue;
475
476 if (conflictMask & instance->holdMask)
477 {
478 /* hard block: blocked by lock already held by this entry */
479 }
480 else if (instance->waitLockMode != NoLock &&
481 (conflictMask & LOCKBIT_ON(instance->waitLockMode)))
482 {
483 /* conflict in lock requests; who's in front in wait queue? */
484 bool ahead = false;
485 int k;
486
487 for (k = 0; k < bproc->num_waiters; k++)
488 {
489 if (preceding_waiters[k] == instance->pid)
490 {
491 /* soft block: this entry is ahead of blocked proc */
492 ahead = true;
493 break;
494 }
495 }
496 if (!ahead)
497 continue; /* not blocked by this entry */
498 }
499 else
500 {
501 /* not blocked by this entry */
502 continue;
503 }
504
505 /* blocked by this entry, so emit a record */
506 arrayelems[narrayelems++] = Int32GetDatum(instance->leaderPid);
507 }
508 }
509
510 /* Assert we didn't overrun arrayelems[] */
511 Assert(narrayelems <= lockData->nlocks);
512
513 /* Construct array, using hardwired knowledge about int4 type */
514 PG_RETURN_ARRAYTYPE_P(construct_array(arrayelems, narrayelems,
515 INT4OID,
516 sizeof(int32), true, 'i'));
517}
518
519
520/*
521 * pg_safe_snapshot_blocking_pids - produce an array of the PIDs blocking
522 * given PID from getting a safe snapshot
523 *
524 * XXX this does not consider parallel-query cases; not clear how big a
525 * problem that is in practice
526 */
527Datum
528pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
529{
530 int blocked_pid = PG_GETARG_INT32(0);
531 int *blockers;
532 int num_blockers;
533 Datum *blocker_datums;
534
535 /* A buffer big enough for any possible blocker list without truncation */
536 blockers = (int *) palloc(MaxBackends * sizeof(int));
537
538 /* Collect a snapshot of processes waited for by GetSafeSnapshot */
539 num_blockers =
540 GetSafeSnapshotBlockingPids(blocked_pid, blockers, MaxBackends);
541
542 /* Convert int array to Datum array */
543 if (num_blockers > 0)
544 {
545 int i;
546
547 blocker_datums = (Datum *) palloc(num_blockers * sizeof(Datum));
548 for (i = 0; i < num_blockers; ++i)
549 blocker_datums[i] = Int32GetDatum(blockers[i]);
550 }
551 else
552 blocker_datums = NULL;
553
554 /* Construct array, using hardwired knowledge about int4 type */
555 PG_RETURN_ARRAYTYPE_P(construct_array(blocker_datums, num_blockers,
556 INT4OID,
557 sizeof(int32), true, 'i'));
558}
559
560
561/*
562 * pg_isolation_test_session_is_blocked - support function for isolationtester
563 *
564 * Check if specified PID is blocked by any of the PIDs listed in the second
565 * argument. Currently, this looks for blocking caused by waiting for
566 * heavyweight locks or safe snapshots. We ignore blockage caused by PIDs
567 * not directly under the isolationtester's control, eg autovacuum.
568 *
569 * This is an undocumented function intended for use by the isolation tester,
570 * and may change in future releases as required for testing purposes.
571 */
572Datum
573pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
574{
575 int blocked_pid = PG_GETARG_INT32(0);
576 ArrayType *interesting_pids_a = PG_GETARG_ARRAYTYPE_P(1);
577 ArrayType *blocking_pids_a;
578 int32 *interesting_pids;
579 int32 *blocking_pids;
580 int num_interesting_pids;
581 int num_blocking_pids;
582 int dummy;
583 int i,
584 j;
585
586 /* Validate the passed-in array */
587 Assert(ARR_ELEMTYPE(interesting_pids_a) == INT4OID);
588 if (array_contains_nulls(interesting_pids_a))
589 elog(ERROR, "array must not contain nulls");
590 interesting_pids = (int32 *) ARR_DATA_PTR(interesting_pids_a);
591 num_interesting_pids = ArrayGetNItems(ARR_NDIM(interesting_pids_a),
592 ARR_DIMS(interesting_pids_a));
593
594 /*
595 * Get the PIDs of all sessions blocking the given session's attempt to
596 * acquire heavyweight locks.
597 */
598 blocking_pids_a =
599 DatumGetArrayTypeP(DirectFunctionCall1(pg_blocking_pids, blocked_pid));
600
601 Assert(ARR_ELEMTYPE(blocking_pids_a) == INT4OID);
602 Assert(!array_contains_nulls(blocking_pids_a));
603 blocking_pids = (int32 *) ARR_DATA_PTR(blocking_pids_a);
604 num_blocking_pids = ArrayGetNItems(ARR_NDIM(blocking_pids_a),
605 ARR_DIMS(blocking_pids_a));
606
607 /*
608 * Check if any of these are in the list of interesting PIDs, that being
609 * the sessions that the isolation tester is running. We don't use
610 * "arrayoverlaps" here, because it would lead to cache lookups and one of
611 * our goals is to run quickly under CLOBBER_CACHE_ALWAYS. We expect
612 * blocking_pids to be usually empty and otherwise a very small number in
613 * isolation tester cases, so make that the outer loop of a naive search
614 * for a match.
615 */
616 for (i = 0; i < num_blocking_pids; i++)
617 for (j = 0; j < num_interesting_pids; j++)
618 {
619 if (blocking_pids[i] == interesting_pids[j])
620 PG_RETURN_BOOL(true);
621 }
622
623 /*
624 * Check if blocked_pid is waiting for a safe snapshot. We could in
625 * theory check the resulting array of blocker PIDs against the
626 * interesting PIDs whitelist, but since there is no danger of autovacuum
627 * blocking GetSafeSnapshot there seems to be no point in expending cycles
628 * on allocating a buffer and searching for overlap; so it's presently
629 * sufficient for the isolation tester's purposes to use a single element
630 * buffer and check if the number of safe snapshot blockers is non-zero.
631 */
632 if (GetSafeSnapshotBlockingPids(blocked_pid, &dummy, 1) > 0)
633 PG_RETURN_BOOL(true);
634
635 PG_RETURN_BOOL(false);
636}
637
638
639/*
640 * Functions for manipulating advisory locks
641 *
642 * We make use of the locktag fields as follows:
643 *
644 * field1: MyDatabaseId ... ensures locks are local to each database
645 * field2: first of 2 int4 keys, or high-order half of an int8 key
646 * field3: second of 2 int4 keys, or low-order half of an int8 key
647 * field4: 1 if using an int8 key, 2 if using 2 int4 keys
648 */
649#define SET_LOCKTAG_INT64(tag, key64) \
650 SET_LOCKTAG_ADVISORY(tag, \
651 MyDatabaseId, \
652 (uint32) ((key64) >> 32), \
653 (uint32) (key64), \
654 1)
655#define SET_LOCKTAG_INT32(tag, key1, key2) \
656 SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, key1, key2, 2)
657
658static void
659PreventAdvisoryLocksInParallelMode(void)
660{
661 if (IsInParallelMode())
662 ereport(ERROR,
663 (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
664 errmsg("cannot use advisory locks during a parallel operation")));
665}
666
667/*
668 * pg_advisory_lock(int8) - acquire exclusive lock on an int8 key
669 */
670Datum
671pg_advisory_lock_int8(PG_FUNCTION_ARGS)
672{
673 int64 key = PG_GETARG_INT64(0);
674 LOCKTAG tag;
675
676 PreventAdvisoryLocksInParallelMode();
677 SET_LOCKTAG_INT64(tag, key);
678
679 (void) LockAcquire(&tag, ExclusiveLock, true, false);
680
681 PG_RETURN_VOID();
682}
683
684/*
685 * pg_advisory_xact_lock(int8) - acquire xact scoped
686 * exclusive lock on an int8 key
687 */
688Datum
689pg_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
690{
691 int64 key = PG_GETARG_INT64(0);
692 LOCKTAG tag;
693
694 PreventAdvisoryLocksInParallelMode();
695 SET_LOCKTAG_INT64(tag, key);
696
697 (void) LockAcquire(&tag, ExclusiveLock, false, false);
698
699 PG_RETURN_VOID();
700}
701
702/*
703 * pg_advisory_lock_shared(int8) - acquire share lock on an int8 key
704 */
705Datum
706pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
707{
708 int64 key = PG_GETARG_INT64(0);
709 LOCKTAG tag;
710
711 PreventAdvisoryLocksInParallelMode();
712 SET_LOCKTAG_INT64(tag, key);
713
714 (void) LockAcquire(&tag, ShareLock, true, false);
715
716 PG_RETURN_VOID();
717}
718
719/*
720 * pg_advisory_xact_lock_shared(int8) - acquire xact scoped
721 * share lock on an int8 key
722 */
723Datum
724pg_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
725{
726 int64 key = PG_GETARG_INT64(0);
727 LOCKTAG tag;
728
729 PreventAdvisoryLocksInParallelMode();
730 SET_LOCKTAG_INT64(tag, key);
731
732 (void) LockAcquire(&tag, ShareLock, false, false);
733
734 PG_RETURN_VOID();
735}
736
737/*
738 * pg_try_advisory_lock(int8) - acquire exclusive lock on an int8 key, no wait
739 *
740 * Returns true if successful, false if lock not available
741 */
742Datum
743pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
744{
745 int64 key = PG_GETARG_INT64(0);
746 LOCKTAG tag;
747 LockAcquireResult res;
748
749 PreventAdvisoryLocksInParallelMode();
750 SET_LOCKTAG_INT64(tag, key);
751
752 res = LockAcquire(&tag, ExclusiveLock, true, true);
753
754 PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
755}
756
757/*
758 * pg_try_advisory_xact_lock(int8) - acquire xact scoped
759 * exclusive lock on an int8 key, no wait
760 *
761 * Returns true if successful, false if lock not available
762 */
763Datum
764pg_try_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
765{
766 int64 key = PG_GETARG_INT64(0);
767 LOCKTAG tag;
768 LockAcquireResult res;
769
770 PreventAdvisoryLocksInParallelMode();
771 SET_LOCKTAG_INT64(tag, key);
772
773 res = LockAcquire(&tag, ExclusiveLock, false, true);
774
775 PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
776}
777
778/*
779 * pg_try_advisory_lock_shared(int8) - acquire share lock on an int8 key, no wait
780 *
781 * Returns true if successful, false if lock not available
782 */
783Datum
784pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
785{
786 int64 key = PG_GETARG_INT64(0);
787 LOCKTAG tag;
788 LockAcquireResult res;
789
790 PreventAdvisoryLocksInParallelMode();
791 SET_LOCKTAG_INT64(tag, key);
792
793 res = LockAcquire(&tag, ShareLock, true, true);
794
795 PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
796}
797
798/*
799 * pg_try_advisory_xact_lock_shared(int8) - acquire xact scoped
800 * share lock on an int8 key, no wait
801 *
802 * Returns true if successful, false if lock not available
803 */
804Datum
805pg_try_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
806{
807 int64 key = PG_GETARG_INT64(0);
808 LOCKTAG tag;
809 LockAcquireResult res;
810
811 PreventAdvisoryLocksInParallelMode();
812 SET_LOCKTAG_INT64(tag, key);
813
814 res = LockAcquire(&tag, ShareLock, false, true);
815
816 PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
817}
818
819/*
820 * pg_advisory_unlock(int8) - release exclusive lock on an int8 key
821 *
822 * Returns true if successful, false if lock was not held
823*/
824Datum
825pg_advisory_unlock_int8(PG_FUNCTION_ARGS)
826{
827 int64 key = PG_GETARG_INT64(0);
828 LOCKTAG tag;
829 bool res;
830
831 PreventAdvisoryLocksInParallelMode();
832 SET_LOCKTAG_INT64(tag, key);
833
834 res = LockRelease(&tag, ExclusiveLock, true);
835
836 PG_RETURN_BOOL(res);
837}
838
839/*
840 * pg_advisory_unlock_shared(int8) - release share lock on an int8 key
841 *
842 * Returns true if successful, false if lock was not held
843 */
844Datum
845pg_advisory_unlock_shared_int8(PG_FUNCTION_ARGS)
846{
847 int64 key = PG_GETARG_INT64(0);
848 LOCKTAG tag;
849 bool res;
850
851 PreventAdvisoryLocksInParallelMode();
852 SET_LOCKTAG_INT64(tag, key);
853
854 res = LockRelease(&tag, ShareLock, true);
855
856 PG_RETURN_BOOL(res);
857}
858
859/*
860 * pg_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys
861 */
862Datum
863pg_advisory_lock_int4(PG_FUNCTION_ARGS)
864{
865 int32 key1 = PG_GETARG_INT32(0);
866 int32 key2 = PG_GETARG_INT32(1);
867 LOCKTAG tag;
868
869 PreventAdvisoryLocksInParallelMode();
870 SET_LOCKTAG_INT32(tag, key1, key2);
871
872 (void) LockAcquire(&tag, ExclusiveLock, true, false);
873
874 PG_RETURN_VOID();
875}
876
877/*
878 * pg_advisory_xact_lock(int4, int4) - acquire xact scoped
879 * exclusive lock on 2 int4 keys
880 */
881Datum
882pg_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
883{
884 int32 key1 = PG_GETARG_INT32(0);
885 int32 key2 = PG_GETARG_INT32(1);
886 LOCKTAG tag;
887
888 PreventAdvisoryLocksInParallelMode();
889 SET_LOCKTAG_INT32(tag, key1, key2);
890
891 (void) LockAcquire(&tag, ExclusiveLock, false, false);
892
893 PG_RETURN_VOID();
894}
895
896/*
897 * pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys
898 */
899Datum
900pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
901{
902 int32 key1 = PG_GETARG_INT32(0);
903 int32 key2 = PG_GETARG_INT32(1);
904 LOCKTAG tag;
905
906 PreventAdvisoryLocksInParallelMode();
907 SET_LOCKTAG_INT32(tag, key1, key2);
908
909 (void) LockAcquire(&tag, ShareLock, true, false);
910
911 PG_RETURN_VOID();
912}
913
914/*
915 * pg_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
916 * share lock on 2 int4 keys
917 */
918Datum
919pg_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
920{
921 int32 key1 = PG_GETARG_INT32(0);
922 int32 key2 = PG_GETARG_INT32(1);
923 LOCKTAG tag;
924
925 PreventAdvisoryLocksInParallelMode();
926 SET_LOCKTAG_INT32(tag, key1, key2);
927
928 (void) LockAcquire(&tag, ShareLock, false, false);
929
930 PG_RETURN_VOID();
931}
932
933/*
934 * pg_try_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys, no wait
935 *
936 * Returns true if successful, false if lock not available
937 */
938Datum
939pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
940{
941 int32 key1 = PG_GETARG_INT32(0);
942 int32 key2 = PG_GETARG_INT32(1);
943 LOCKTAG tag;
944 LockAcquireResult res;
945
946 PreventAdvisoryLocksInParallelMode();
947 SET_LOCKTAG_INT32(tag, key1, key2);
948
949 res = LockAcquire(&tag, ExclusiveLock, true, true);
950
951 PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
952}
953
954/*
955 * pg_try_advisory_xact_lock(int4, int4) - acquire xact scoped
956 * exclusive lock on 2 int4 keys, no wait
957 *
958 * Returns true if successful, false if lock not available
959 */
960Datum
961pg_try_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
962{
963 int32 key1 = PG_GETARG_INT32(0);
964 int32 key2 = PG_GETARG_INT32(1);
965 LOCKTAG tag;
966 LockAcquireResult res;
967
968 PreventAdvisoryLocksInParallelMode();
969 SET_LOCKTAG_INT32(tag, key1, key2);
970
971 res = LockAcquire(&tag, ExclusiveLock, false, true);
972
973 PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
974}
975
976/*
977 * pg_try_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys, no wait
978 *
979 * Returns true if successful, false if lock not available
980 */
981Datum
982pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
983{
984 int32 key1 = PG_GETARG_INT32(0);
985 int32 key2 = PG_GETARG_INT32(1);
986 LOCKTAG tag;
987 LockAcquireResult res;
988
989 PreventAdvisoryLocksInParallelMode();
990 SET_LOCKTAG_INT32(tag, key1, key2);
991
992 res = LockAcquire(&tag, ShareLock, true, true);
993
994 PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
995}
996
997/*
998 * pg_try_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
999 * share lock on 2 int4 keys, no wait
1000 *
1001 * Returns true if successful, false if lock not available
1002 */
1003Datum
1004pg_try_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
1005{
1006 int32 key1 = PG_GETARG_INT32(0);
1007 int32 key2 = PG_GETARG_INT32(1);
1008 LOCKTAG tag;
1009 LockAcquireResult res;
1010
1011 PreventAdvisoryLocksInParallelMode();
1012 SET_LOCKTAG_INT32(tag, key1, key2);
1013
1014 res = LockAcquire(&tag, ShareLock, false, true);
1015
1016 PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
1017}
1018
1019/*
1020 * pg_advisory_unlock(int4, int4) - release exclusive lock on 2 int4 keys
1021 *
1022 * Returns true if successful, false if lock was not held
1023*/
1024Datum
1025pg_advisory_unlock_int4(PG_FUNCTION_ARGS)
1026{
1027 int32 key1 = PG_GETARG_INT32(0);
1028 int32 key2 = PG_GETARG_INT32(1);
1029 LOCKTAG tag;
1030 bool res;
1031
1032 PreventAdvisoryLocksInParallelMode();
1033 SET_LOCKTAG_INT32(tag, key1, key2);
1034
1035 res = LockRelease(&tag, ExclusiveLock, true);
1036
1037 PG_RETURN_BOOL(res);
1038}
1039
1040/*
1041 * pg_advisory_unlock_shared(int4, int4) - release share lock on 2 int4 keys
1042 *
1043 * Returns true if successful, false if lock was not held
1044 */
1045Datum
1046pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
1047{
1048 int32 key1 = PG_GETARG_INT32(0);
1049 int32 key2 = PG_GETARG_INT32(1);
1050 LOCKTAG tag;
1051 bool res;
1052
1053 PreventAdvisoryLocksInParallelMode();
1054 SET_LOCKTAG_INT32(tag, key1, key2);
1055
1056 res = LockRelease(&tag, ShareLock, true);
1057
1058 PG_RETURN_BOOL(res);
1059}
1060
1061/*
1062 * pg_advisory_unlock_all() - release all advisory locks
1063 */
1064Datum
1065pg_advisory_unlock_all(PG_FUNCTION_ARGS)
1066{
1067 LockReleaseSession(USER_LOCKMETHOD);
1068
1069 PG_RETURN_VOID();
1070}
1071