Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * lockfuncs.c
4 : * Functions for SQL access to various lock-manager capabilities.
5 : *
6 : * Copyright (c) 2002-2017, 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! */
26 : const 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) */
40 : static const char *const PredicateLockTagTypeNames[] = {
41 : "relation",
42 : "page",
43 : "tuple"
44 : };
45 :
46 : /* Working status for pg_lock_status */
47 : typedef 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 : */
63 : static Datum
64 1171 : VXIDGetDatum(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 1171 : snprintf(vxidstr, sizeof(vxidstr), "%d/%u", bid, lxid);
73 :
74 1171 : return CStringGetTextDatum(vxidstr);
75 : }
76 :
77 :
78 : /*
79 : * pg_lock_status - produce a view with one row per held or awaited lock mode
80 : */
81 : Datum
82 1068 : pg_lock_status(PG_FUNCTION_ARGS)
83 : {
84 : FuncCallContext *funcctx;
85 : PG_Lock_Status *mystatus;
86 : LockData *lockData;
87 : PredicateLockData *predLockData;
88 :
89 1068 : if (SRF_IS_FIRSTCALL())
90 : {
91 : TupleDesc tupdesc;
92 : MemoryContext oldcontext;
93 :
94 : /* create a function context for cross-call persistence */
95 52 : funcctx = SRF_FIRSTCALL_INIT();
96 :
97 : /*
98 : * switch to memory context appropriate for multiple function calls
99 : */
100 52 : 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 52 : tupdesc = CreateTemplateTupleDesc(NUM_LOCK_STATUS_COLUMNS, false);
105 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype",
106 : TEXTOID, -1, 0);
107 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
108 : OIDOID, -1, 0);
109 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relation",
110 : OIDOID, -1, 0);
111 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 4, "page",
112 : INT4OID, -1, 0);
113 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 5, "tuple",
114 : INT2OID, -1, 0);
115 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 6, "virtualxid",
116 : TEXTOID, -1, 0);
117 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 7, "transactionid",
118 : XIDOID, -1, 0);
119 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 8, "classid",
120 : OIDOID, -1, 0);
121 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objid",
122 : OIDOID, -1, 0);
123 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 10, "objsubid",
124 : INT2OID, -1, 0);
125 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 11, "virtualtransaction",
126 : TEXTOID, -1, 0);
127 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 12, "pid",
128 : INT4OID, -1, 0);
129 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 13, "mode",
130 : TEXTOID, -1, 0);
131 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 14, "granted",
132 : BOOLOID, -1, 0);
133 52 : TupleDescInitEntry(tupdesc, (AttrNumber) 15, "fastpath",
134 : BOOLOID, -1, 0);
135 :
136 52 : 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 52 : mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status));
143 52 : funcctx->user_fctx = (void *) mystatus;
144 :
145 52 : mystatus->lockData = GetLockStatusData();
146 52 : mystatus->currIdx = 0;
147 52 : mystatus->predLockData = GetPredicateLockStatusData();
148 52 : mystatus->predLockIdx = 0;
149 :
150 52 : MemoryContextSwitchTo(oldcontext);
151 : }
152 :
153 1068 : funcctx = SRF_PERCALL_SETUP();
154 1068 : mystatus = (PG_Lock_Status *) funcctx->user_fctx;
155 1068 : lockData = mystatus->lockData;
156 :
157 3135 : while (mystatus->currIdx < lockData->nelements)
158 : {
159 : bool granted;
160 2015 : 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 2015 : 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 2015 : granted = false;
177 2015 : if (instance->holdMask)
178 : {
179 3949 : for (mode = 0; mode < MAX_LOCKMODES; mode++)
180 : {
181 3949 : if (instance->holdMask & LOCKBIT_ON(mode))
182 : {
183 1016 : granted = true;
184 1016 : instance->holdMask &= LOCKBIT_OFF(mode);
185 1016 : 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 2015 : if (!granted)
195 : {
196 999 : if (instance->waitLockMode != NoLock)
197 : {
198 : /* Yes, so report it with proper mode */
199 0 : 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 0 : 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 999 : mystatus->currIdx++;
214 999 : continue;
215 : }
216 : }
217 :
218 : /*
219 : * Form tuple with appropriate data.
220 : */
221 1016 : MemSet(values, 0, sizeof(values));
222 1016 : MemSet(nulls, false, sizeof(nulls));
223 :
224 1016 : if (instance->locktag.locktag_type <= LOCKTAG_LAST_TYPE)
225 1016 : locktypename = LockTagTypeNames[instance->locktag.locktag_type];
226 : else
227 : {
228 0 : snprintf(tnbuf, sizeof(tnbuf), "unknown %d",
229 0 : (int) instance->locktag.locktag_type);
230 0 : locktypename = tnbuf;
231 : }
232 1016 : values[0] = CStringGetTextDatum(locktypename);
233 :
234 1016 : switch ((LockTagType) instance->locktag.locktag_type)
235 : {
236 : case LOCKTAG_RELATION:
237 : case LOCKTAG_RELATION_EXTEND:
238 746 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
239 746 : values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
240 746 : nulls[3] = true;
241 746 : nulls[4] = true;
242 746 : nulls[5] = true;
243 746 : nulls[6] = true;
244 746 : nulls[7] = true;
245 746 : nulls[8] = true;
246 746 : nulls[9] = true;
247 746 : break;
248 : case LOCKTAG_PAGE:
249 0 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
250 0 : values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
251 0 : values[3] = UInt32GetDatum(instance->locktag.locktag_field3);
252 0 : nulls[4] = true;
253 0 : nulls[5] = true;
254 0 : nulls[6] = true;
255 0 : nulls[7] = true;
256 0 : nulls[8] = true;
257 0 : nulls[9] = true;
258 0 : break;
259 : case LOCKTAG_TUPLE:
260 0 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
261 0 : values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
262 0 : values[3] = UInt32GetDatum(instance->locktag.locktag_field3);
263 0 : values[4] = UInt16GetDatum(instance->locktag.locktag_field4);
264 0 : nulls[5] = true;
265 0 : nulls[6] = true;
266 0 : nulls[7] = true;
267 0 : nulls[8] = true;
268 0 : nulls[9] = true;
269 0 : break;
270 : case LOCKTAG_TRANSACTION:
271 73 : values[6] =
272 73 : TransactionIdGetDatum(instance->locktag.locktag_field1);
273 73 : nulls[1] = true;
274 73 : nulls[2] = true;
275 73 : nulls[3] = true;
276 73 : nulls[4] = true;
277 73 : nulls[5] = true;
278 73 : nulls[7] = true;
279 73 : nulls[8] = true;
280 73 : nulls[9] = true;
281 73 : break;
282 : case LOCKTAG_VIRTUALTRANSACTION:
283 155 : values[5] = VXIDGetDatum(instance->locktag.locktag_field1,
284 : instance->locktag.locktag_field2);
285 155 : nulls[1] = true;
286 155 : nulls[2] = true;
287 155 : nulls[3] = true;
288 155 : nulls[4] = true;
289 155 : nulls[6] = true;
290 155 : nulls[7] = true;
291 155 : nulls[8] = true;
292 155 : nulls[9] = true;
293 155 : break;
294 : case LOCKTAG_OBJECT:
295 : case LOCKTAG_USERLOCK:
296 : case LOCKTAG_ADVISORY:
297 : default: /* treat unknown locktags like OBJECT */
298 42 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
299 42 : values[7] = ObjectIdGetDatum(instance->locktag.locktag_field2);
300 42 : values[8] = ObjectIdGetDatum(instance->locktag.locktag_field3);
301 42 : values[9] = Int16GetDatum(instance->locktag.locktag_field4);
302 42 : nulls[2] = true;
303 42 : nulls[3] = true;
304 42 : nulls[4] = true;
305 42 : nulls[5] = true;
306 42 : nulls[6] = true;
307 42 : break;
308 : }
309 :
310 1016 : values[10] = VXIDGetDatum(instance->backend, instance->lxid);
311 1016 : if (instance->pid != 0)
312 1016 : values[11] = Int32GetDatum(instance->pid);
313 : else
314 0 : nulls[11] = true;
315 1016 : values[12] = CStringGetTextDatum(GetLockmodeName(instance->locktag.locktag_lockmethodid, mode));
316 1016 : values[13] = BoolGetDatum(granted);
317 1016 : values[14] = BoolGetDatum(instance->fastpath);
318 :
319 1016 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
320 1016 : result = HeapTupleGetDatum(tuple);
321 1016 : SRF_RETURN_NEXT(funcctx, result);
322 : }
323 :
324 : /*
325 : * Have returned all regular locks. Now start on the SIREAD predicate
326 : * locks.
327 : */
328 52 : predLockData = mystatus->predLockData;
329 52 : if (mystatus->predLockIdx < predLockData->nelements)
330 : {
331 : PredicateLockTargetType lockType;
332 :
333 0 : PREDICATELOCKTARGETTAG *predTag = &(predLockData->locktags[mystatus->predLockIdx]);
334 0 : 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 0 : mystatus->predLockIdx++;
341 :
342 : /*
343 : * Form tuple with appropriate data.
344 : */
345 0 : MemSet(values, 0, sizeof(values));
346 0 : MemSet(nulls, false, sizeof(nulls));
347 :
348 : /* lock type */
349 0 : lockType = GET_PREDICATELOCKTARGETTAG_TYPE(*predTag);
350 :
351 0 : values[0] = CStringGetTextDatum(PredicateLockTagTypeNames[lockType]);
352 :
353 : /* lock target */
354 0 : values[1] = GET_PREDICATELOCKTARGETTAG_DB(*predTag);
355 0 : values[2] = GET_PREDICATELOCKTARGETTAG_RELATION(*predTag);
356 0 : if (lockType == PREDLOCKTAG_TUPLE)
357 0 : values[4] = GET_PREDICATELOCKTARGETTAG_OFFSET(*predTag);
358 : else
359 0 : nulls[4] = true;
360 0 : if ((lockType == PREDLOCKTAG_TUPLE) ||
361 : (lockType == PREDLOCKTAG_PAGE))
362 0 : values[3] = GET_PREDICATELOCKTARGETTAG_PAGE(*predTag);
363 : else
364 0 : nulls[3] = true;
365 :
366 : /* these fields are targets for other types of locks */
367 0 : nulls[5] = true; /* virtualxid */
368 0 : nulls[6] = true; /* transactionid */
369 0 : nulls[7] = true; /* classid */
370 0 : nulls[8] = true; /* objid */
371 0 : nulls[9] = true; /* objsubid */
372 :
373 : /* lock holder */
374 0 : values[10] = VXIDGetDatum(xact->vxid.backendId,
375 : xact->vxid.localTransactionId);
376 0 : if (xact->pid != 0)
377 0 : values[11] = Int32GetDatum(xact->pid);
378 : else
379 0 : 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 0 : values[12] = CStringGetTextDatum("SIReadLock");
386 0 : values[13] = BoolGetDatum(true);
387 0 : values[14] = BoolGetDatum(false);
388 :
389 0 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
390 0 : result = HeapTupleGetDatum(tuple);
391 0 : SRF_RETURN_NEXT(funcctx, result);
392 : }
393 :
394 52 : 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 : */
417 : Datum
418 0 : pg_blocking_pids(PG_FUNCTION_ARGS)
419 : {
420 0 : 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 0 : lockData = GetBlockerStatusData(blocked_pid);
429 :
430 : /* We can't need more output entries than there are reported PROCLOCKs */
431 0 : arrayelems = (Datum *) palloc(lockData->nlocks * sizeof(Datum));
432 0 : narrayelems = 0;
433 :
434 : /* For each blocked proc in the lock group ... */
435 0 : for (i = 0; i < lockData->nprocs; i++)
436 : {
437 0 : BlockedProcData *bproc = &lockData->procs[i];
438 0 : LockInstanceData *instances = &lockData->locks[bproc->first_lock];
439 0 : 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 0 : blocked_instance = NULL;
449 0 : for (j = 0; j < bproc->num_locks; j++)
450 : {
451 0 : LockInstanceData *instance = &(instances[j]);
452 :
453 0 : if (instance->pid == bproc->pid)
454 : {
455 0 : Assert(blocked_instance == NULL);
456 0 : blocked_instance = instance;
457 : }
458 : }
459 0 : Assert(blocked_instance != NULL);
460 :
461 0 : lockMethodTable = GetLockTagsMethodTable(&(blocked_instance->locktag));
462 0 : conflictMask = lockMethodTable->conflictTab[blocked_instance->waitLockMode];
463 :
464 : /* Now scan the PROCLOCK data for conflicting procs */
465 0 : for (j = 0; j < bproc->num_locks; j++)
466 : {
467 0 : LockInstanceData *instance = &(instances[j]);
468 :
469 : /* A proc never blocks itself, so ignore that entry */
470 0 : if (instance == blocked_instance)
471 0 : continue;
472 : /* Members of same lock group never block each other, either */
473 0 : if (instance->leaderPid == blocked_instance->leaderPid)
474 0 : continue;
475 :
476 0 : if (conflictMask & instance->holdMask)
477 : {
478 : /* hard block: blocked by lock already held by this entry */
479 : }
480 0 : else if (instance->waitLockMode != NoLock &&
481 0 : (conflictMask & LOCKBIT_ON(instance->waitLockMode)))
482 0 : {
483 : /* conflict in lock requests; who's in front in wait queue? */
484 0 : bool ahead = false;
485 : int k;
486 :
487 0 : for (k = 0; k < bproc->num_waiters; k++)
488 : {
489 0 : if (preceding_waiters[k] == instance->pid)
490 : {
491 : /* soft block: this entry is ahead of blocked proc */
492 0 : ahead = true;
493 0 : break;
494 : }
495 : }
496 0 : if (!ahead)
497 0 : continue; /* not blocked by this entry */
498 : }
499 : else
500 : {
501 : /* not blocked by this entry */
502 0 : continue;
503 : }
504 :
505 : /* blocked by this entry, so emit a record */
506 0 : arrayelems[narrayelems++] = Int32GetDatum(instance->leaderPid);
507 : }
508 : }
509 :
510 : /* Assert we didn't overrun arrayelems[] */
511 0 : Assert(narrayelems <= lockData->nlocks);
512 :
513 : /* Construct array, using hardwired knowledge about int4 type */
514 0 : 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 : */
527 : Datum
528 0 : pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
529 : {
530 0 : 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 0 : blockers = (int *) palloc(MaxBackends * sizeof(int));
537 :
538 : /* Collect a snapshot of processes waited for by GetSafeSnapshot */
539 0 : num_blockers =
540 0 : GetSafeSnapshotBlockingPids(blocked_pid, blockers, MaxBackends);
541 :
542 : /* Convert int array to Datum array */
543 0 : if (num_blockers > 0)
544 : {
545 : int i;
546 :
547 0 : blocker_datums = (Datum *) palloc(num_blockers * sizeof(Datum));
548 0 : for (i = 0; i < num_blockers; ++i)
549 0 : blocker_datums[i] = Int32GetDatum(blockers[i]);
550 : }
551 : else
552 0 : blocker_datums = NULL;
553 :
554 : /* Construct array, using hardwired knowledge about int4 type */
555 0 : 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 : */
572 : Datum
573 0 : pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
574 : {
575 0 : int blocked_pid = PG_GETARG_INT32(0);
576 0 : 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 0 : Assert(ARR_ELEMTYPE(interesting_pids_a) == INT4OID);
588 0 : if (array_contains_nulls(interesting_pids_a))
589 0 : elog(ERROR, "array must not contain nulls");
590 0 : interesting_pids = (int32 *) ARR_DATA_PTR(interesting_pids_a);
591 0 : 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 0 : blocking_pids_a =
599 0 : DatumGetArrayTypeP(DirectFunctionCall1(pg_blocking_pids, blocked_pid));
600 :
601 0 : Assert(ARR_ELEMTYPE(blocking_pids_a) == INT4OID);
602 0 : Assert(!array_contains_nulls(blocking_pids_a));
603 0 : blocking_pids = (int32 *) ARR_DATA_PTR(blocking_pids_a);
604 0 : 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 0 : for (i = 0; i < num_blocking_pids; i++)
617 0 : for (j = 0; j < num_interesting_pids; j++)
618 : {
619 0 : if (blocking_pids[i] == interesting_pids[j])
620 0 : 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 0 : if (GetSafeSnapshotBlockingPids(blocked_pid, &dummy, 1) > 0)
633 0 : PG_RETURN_BOOL(true);
634 :
635 0 : 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 :
658 : static void
659 64 : PreventAdvisoryLocksInParallelMode(void)
660 : {
661 64 : if (IsInParallelMode())
662 0 : ereport(ERROR,
663 : (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
664 : errmsg("cannot use advisory locks during a parallel operation")));
665 64 : }
666 :
667 : /*
668 : * pg_advisory_lock(int8) - acquire exclusive lock on an int8 key
669 : */
670 : Datum
671 6 : pg_advisory_lock_int8(PG_FUNCTION_ARGS)
672 : {
673 6 : int64 key = PG_GETARG_INT64(0);
674 : LOCKTAG tag;
675 :
676 6 : PreventAdvisoryLocksInParallelMode();
677 6 : SET_LOCKTAG_INT64(tag, key);
678 :
679 6 : (void) LockAcquire(&tag, ExclusiveLock, true, false);
680 :
681 6 : PG_RETURN_VOID();
682 : }
683 :
684 : /*
685 : * pg_advisory_xact_lock(int8) - acquire xact scoped
686 : * exclusive lock on an int8 key
687 : */
688 : Datum
689 5 : pg_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
690 : {
691 5 : int64 key = PG_GETARG_INT64(0);
692 : LOCKTAG tag;
693 :
694 5 : PreventAdvisoryLocksInParallelMode();
695 5 : SET_LOCKTAG_INT64(tag, key);
696 :
697 5 : (void) LockAcquire(&tag, ExclusiveLock, false, false);
698 :
699 5 : PG_RETURN_VOID();
700 : }
701 :
702 : /*
703 : * pg_advisory_lock_shared(int8) - acquire share lock on an int8 key
704 : */
705 : Datum
706 6 : pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
707 : {
708 6 : int64 key = PG_GETARG_INT64(0);
709 : LOCKTAG tag;
710 :
711 6 : PreventAdvisoryLocksInParallelMode();
712 6 : SET_LOCKTAG_INT64(tag, key);
713 :
714 6 : (void) LockAcquire(&tag, ShareLock, true, false);
715 :
716 6 : PG_RETURN_VOID();
717 : }
718 :
719 : /*
720 : * pg_advisory_xact_lock_shared(int8) - acquire xact scoped
721 : * share lock on an int8 key
722 : */
723 : Datum
724 5 : pg_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
725 : {
726 5 : int64 key = PG_GETARG_INT64(0);
727 : LOCKTAG tag;
728 :
729 5 : PreventAdvisoryLocksInParallelMode();
730 5 : SET_LOCKTAG_INT64(tag, key);
731 :
732 5 : (void) LockAcquire(&tag, ShareLock, false, false);
733 :
734 5 : 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 : */
742 : Datum
743 0 : pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
744 : {
745 0 : int64 key = PG_GETARG_INT64(0);
746 : LOCKTAG tag;
747 : LockAcquireResult res;
748 :
749 0 : PreventAdvisoryLocksInParallelMode();
750 0 : SET_LOCKTAG_INT64(tag, key);
751 :
752 0 : res = LockAcquire(&tag, ExclusiveLock, true, true);
753 :
754 0 : 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 : */
763 : Datum
764 0 : pg_try_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
765 : {
766 0 : int64 key = PG_GETARG_INT64(0);
767 : LOCKTAG tag;
768 : LockAcquireResult res;
769 :
770 0 : PreventAdvisoryLocksInParallelMode();
771 0 : SET_LOCKTAG_INT64(tag, key);
772 :
773 0 : res = LockAcquire(&tag, ExclusiveLock, false, true);
774 :
775 0 : 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 : */
783 : Datum
784 0 : pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
785 : {
786 0 : int64 key = PG_GETARG_INT64(0);
787 : LOCKTAG tag;
788 : LockAcquireResult res;
789 :
790 0 : PreventAdvisoryLocksInParallelMode();
791 0 : SET_LOCKTAG_INT64(tag, key);
792 :
793 0 : res = LockAcquire(&tag, ShareLock, true, true);
794 :
795 0 : 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 : */
804 : Datum
805 0 : pg_try_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
806 : {
807 0 : int64 key = PG_GETARG_INT64(0);
808 : LOCKTAG tag;
809 : LockAcquireResult res;
810 :
811 0 : PreventAdvisoryLocksInParallelMode();
812 0 : SET_LOCKTAG_INT64(tag, key);
813 :
814 0 : res = LockAcquire(&tag, ShareLock, false, true);
815 :
816 0 : 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 : */
824 : Datum
825 5 : pg_advisory_unlock_int8(PG_FUNCTION_ARGS)
826 : {
827 5 : int64 key = PG_GETARG_INT64(0);
828 : LOCKTAG tag;
829 : bool res;
830 :
831 5 : PreventAdvisoryLocksInParallelMode();
832 5 : SET_LOCKTAG_INT64(tag, key);
833 :
834 5 : res = LockRelease(&tag, ExclusiveLock, true);
835 :
836 5 : 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 : */
844 : Datum
845 5 : pg_advisory_unlock_shared_int8(PG_FUNCTION_ARGS)
846 : {
847 5 : int64 key = PG_GETARG_INT64(0);
848 : LOCKTAG tag;
849 : bool res;
850 :
851 5 : PreventAdvisoryLocksInParallelMode();
852 5 : SET_LOCKTAG_INT64(tag, key);
853 :
854 5 : res = LockRelease(&tag, ShareLock, true);
855 :
856 5 : PG_RETURN_BOOL(res);
857 : }
858 :
859 : /*
860 : * pg_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys
861 : */
862 : Datum
863 6 : pg_advisory_lock_int4(PG_FUNCTION_ARGS)
864 : {
865 6 : int32 key1 = PG_GETARG_INT32(0);
866 6 : int32 key2 = PG_GETARG_INT32(1);
867 : LOCKTAG tag;
868 :
869 6 : PreventAdvisoryLocksInParallelMode();
870 6 : SET_LOCKTAG_INT32(tag, key1, key2);
871 :
872 6 : (void) LockAcquire(&tag, ExclusiveLock, true, false);
873 :
874 6 : PG_RETURN_VOID();
875 : }
876 :
877 : /*
878 : * pg_advisory_xact_lock(int4, int4) - acquire xact scoped
879 : * exclusive lock on 2 int4 keys
880 : */
881 : Datum
882 5 : pg_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
883 : {
884 5 : int32 key1 = PG_GETARG_INT32(0);
885 5 : int32 key2 = PG_GETARG_INT32(1);
886 : LOCKTAG tag;
887 :
888 5 : PreventAdvisoryLocksInParallelMode();
889 5 : SET_LOCKTAG_INT32(tag, key1, key2);
890 :
891 5 : (void) LockAcquire(&tag, ExclusiveLock, false, false);
892 :
893 5 : PG_RETURN_VOID();
894 : }
895 :
896 : /*
897 : * pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys
898 : */
899 : Datum
900 6 : pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
901 : {
902 6 : int32 key1 = PG_GETARG_INT32(0);
903 6 : int32 key2 = PG_GETARG_INT32(1);
904 : LOCKTAG tag;
905 :
906 6 : PreventAdvisoryLocksInParallelMode();
907 6 : SET_LOCKTAG_INT32(tag, key1, key2);
908 :
909 6 : (void) LockAcquire(&tag, ShareLock, true, false);
910 :
911 6 : 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 : */
918 : Datum
919 5 : pg_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
920 : {
921 5 : int32 key1 = PG_GETARG_INT32(0);
922 5 : int32 key2 = PG_GETARG_INT32(1);
923 : LOCKTAG tag;
924 :
925 5 : PreventAdvisoryLocksInParallelMode();
926 5 : SET_LOCKTAG_INT32(tag, key1, key2);
927 :
928 5 : (void) LockAcquire(&tag, ShareLock, false, false);
929 :
930 5 : 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 : */
938 : Datum
939 0 : pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
940 : {
941 0 : int32 key1 = PG_GETARG_INT32(0);
942 0 : int32 key2 = PG_GETARG_INT32(1);
943 : LOCKTAG tag;
944 : LockAcquireResult res;
945 :
946 0 : PreventAdvisoryLocksInParallelMode();
947 0 : SET_LOCKTAG_INT32(tag, key1, key2);
948 :
949 0 : res = LockAcquire(&tag, ExclusiveLock, true, true);
950 :
951 0 : 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 : */
960 : Datum
961 0 : pg_try_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
962 : {
963 0 : int32 key1 = PG_GETARG_INT32(0);
964 0 : int32 key2 = PG_GETARG_INT32(1);
965 : LOCKTAG tag;
966 : LockAcquireResult res;
967 :
968 0 : PreventAdvisoryLocksInParallelMode();
969 0 : SET_LOCKTAG_INT32(tag, key1, key2);
970 :
971 0 : res = LockAcquire(&tag, ExclusiveLock, false, true);
972 :
973 0 : 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 : */
981 : Datum
982 0 : pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
983 : {
984 0 : int32 key1 = PG_GETARG_INT32(0);
985 0 : int32 key2 = PG_GETARG_INT32(1);
986 : LOCKTAG tag;
987 : LockAcquireResult res;
988 :
989 0 : PreventAdvisoryLocksInParallelMode();
990 0 : SET_LOCKTAG_INT32(tag, key1, key2);
991 :
992 0 : res = LockAcquire(&tag, ShareLock, true, true);
993 :
994 0 : 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 : */
1003 : Datum
1004 0 : pg_try_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
1005 : {
1006 0 : int32 key1 = PG_GETARG_INT32(0);
1007 0 : int32 key2 = PG_GETARG_INT32(1);
1008 : LOCKTAG tag;
1009 : LockAcquireResult res;
1010 :
1011 0 : PreventAdvisoryLocksInParallelMode();
1012 0 : SET_LOCKTAG_INT32(tag, key1, key2);
1013 :
1014 0 : res = LockAcquire(&tag, ShareLock, false, true);
1015 :
1016 0 : 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 : */
1024 : Datum
1025 5 : pg_advisory_unlock_int4(PG_FUNCTION_ARGS)
1026 : {
1027 5 : int32 key1 = PG_GETARG_INT32(0);
1028 5 : int32 key2 = PG_GETARG_INT32(1);
1029 : LOCKTAG tag;
1030 : bool res;
1031 :
1032 5 : PreventAdvisoryLocksInParallelMode();
1033 5 : SET_LOCKTAG_INT32(tag, key1, key2);
1034 :
1035 5 : res = LockRelease(&tag, ExclusiveLock, true);
1036 :
1037 5 : 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 : */
1045 : Datum
1046 5 : pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
1047 : {
1048 5 : int32 key1 = PG_GETARG_INT32(0);
1049 5 : int32 key2 = PG_GETARG_INT32(1);
1050 : LOCKTAG tag;
1051 : bool res;
1052 :
1053 5 : PreventAdvisoryLocksInParallelMode();
1054 5 : SET_LOCKTAG_INT32(tag, key1, key2);
1055 :
1056 5 : res = LockRelease(&tag, ShareLock, true);
1057 :
1058 5 : PG_RETURN_BOOL(res);
1059 : }
1060 :
1061 : /*
1062 : * pg_advisory_unlock_all() - release all advisory locks
1063 : */
1064 : Datum
1065 3 : pg_advisory_unlock_all(PG_FUNCTION_ARGS)
1066 : {
1067 3 : LockReleaseSession(USER_LOCKMETHOD);
1068 :
1069 3 : PG_RETURN_VOID();
1070 : }
|