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