Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * lockfuncs.c
4 : : * Functions for SQL access to various lock-manager capabilities.
5 : : *
6 : : * Copyright (c) 2002-2025, 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 "funcapi.h"
17 : : #include "miscadmin.h"
18 : : #include "storage/predicate_internals.h"
19 : : #include "utils/array.h"
20 : : #include "utils/builtins.h"
21 : :
22 : :
23 : : /*
24 : : * This must match enum LockTagType! Also, be sure to document any changes
25 : : * in the docs for the pg_locks view and update the WaitEventLOCK section in
26 : : * src/backend/utils/activity/wait_event_names.txt.
27 : : */
28 : : const char *const LockTagTypeNames[] = {
29 : : "relation",
30 : : "extend",
31 : : "frozenid",
32 : : "page",
33 : : "tuple",
34 : : "transactionid",
35 : : "virtualxid",
36 : : "spectoken",
37 : : "object",
38 : : "userlock",
39 : : "advisory",
40 : : "applytransaction"
41 : : };
42 : :
43 : : StaticAssertDecl(lengthof(LockTagTypeNames) == (LOCKTAG_LAST_TYPE + 1),
44 : : "array length mismatch");
45 : :
46 : : /* This must match enum PredicateLockTargetType (predicate_internals.h) */
47 : : static const char *const PredicateLockTagTypeNames[] = {
48 : : "relation",
49 : : "page",
50 : : "tuple"
51 : : };
52 : :
53 : : StaticAssertDecl(lengthof(PredicateLockTagTypeNames) == (PREDLOCKTAG_TUPLE + 1),
54 : : "array length mismatch");
55 : :
56 : : /* Working status for pg_lock_status */
57 : : typedef struct
58 : : {
59 : : LockData *lockData; /* state data from lmgr */
60 : : int currIdx; /* current PROCLOCK index */
61 : : PredicateLockData *predLockData; /* state data for pred locks */
62 : : int predLockIdx; /* current index for pred lock */
63 : : } PG_Lock_Status;
64 : :
65 : : /* Number of columns in pg_locks output */
66 : : #define NUM_LOCK_STATUS_COLUMNS 16
67 : :
68 : : /*
69 : : * VXIDGetDatum - Construct a text representation of a VXID
70 : : *
71 : : * This is currently only used in pg_lock_status, so we put it here.
72 : : */
73 : : static Datum
552 heikki.linnakangas@i 74 :CBC 11926 : VXIDGetDatum(ProcNumber procNumber, LocalTransactionId lxid)
75 : : {
76 : : /*
77 : : * The representation is "<procNumber>/<lxid>", decimal and unsigned
78 : : * decimal respectively. Note that elog.c also knows how to format a
79 : : * vxid.
80 : : */
81 : : char vxidstr[32];
82 : :
83 : 11926 : snprintf(vxidstr, sizeof(vxidstr), "%d/%u", procNumber, lxid);
84 : :
6374 tgl@sss.pgh.pa.us 85 : 11926 : 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
8411 bruce@momjian.us 93 : 10766 : pg_lock_status(PG_FUNCTION_ARGS)
94 : : {
95 : : FuncCallContext *funcctx;
96 : : PG_Lock_Status *mystatus;
97 : : LockData *lockData;
98 : : PredicateLockData *predLockData;
99 : :
8421 100 [ + + ]: 10766 : if (SRF_IS_FIRSTCALL())
101 : : {
102 : : TupleDesc tupdesc;
103 : : MemoryContext oldcontext;
104 : :
105 : : /* create a function context for cross-call persistence */
8409 tgl@sss.pgh.pa.us 106 : 229 : funcctx = SRF_FIRSTCALL_INIT();
107 : :
108 : : /*
109 : : * switch to memory context appropriate for multiple function calls
110 : : */
111 : 229 : 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 */
2482 andres@anarazel.de 115 : 229 : tupdesc = CreateTemplateTupleDesc(NUM_LOCK_STATUS_COLUMNS);
7417 tgl@sss.pgh.pa.us 116 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype",
117 : : TEXTOID, -1, 0);
8235 bruce@momjian.us 118 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
119 : : OIDOID, -1, 0);
7417 tgl@sss.pgh.pa.us 120 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relation",
121 : : OIDOID, -1, 0);
122 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 4, "page",
123 : : INT4OID, -1, 0);
124 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 5, "tuple",
125 : : INT2OID, -1, 0);
6576 126 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 6, "virtualxid",
127 : : TEXTOID, -1, 0);
128 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 7, "transactionid",
129 : : XIDOID, -1, 0);
130 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 8, "classid",
131 : : OIDOID, -1, 0);
132 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objid",
133 : : OIDOID, -1, 0);
134 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 10, "objsubid",
135 : : INT2OID, -1, 0);
136 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 11, "virtualtransaction",
137 : : TEXTOID, -1, 0);
138 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 12, "pid",
139 : : INT4OID, -1, 0);
140 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 13, "mode",
141 : : TEXTOID, -1, 0);
142 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 14, "granted",
143 : : BOOLOID, -1, 0);
5215 rhaas@postgresql.org 144 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 15, "fastpath",
145 : : BOOLOID, -1, 0);
1664 fujii@postgresql.org 146 : 229 : TupleDescInitEntry(tupdesc, (AttrNumber) 16, "waitstart",
147 : : TIMESTAMPTZOID, -1, 0);
148 : :
7828 tgl@sss.pgh.pa.us 149 : 229 : 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 : : */
8407 155 : 229 : mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status));
282 peter@eisentraut.org 156 : 229 : funcctx->user_fctx = mystatus;
157 : :
8407 tgl@sss.pgh.pa.us 158 : 229 : mystatus->lockData = GetLockStatusData();
159 : 229 : mystatus->currIdx = 0;
5325 heikki.linnakangas@i 160 : 229 : mystatus->predLockData = GetPredicateLockStatusData();
161 : 229 : mystatus->predLockIdx = 0;
162 : :
8409 tgl@sss.pgh.pa.us 163 : 229 : MemoryContextSwitchTo(oldcontext);
164 : : }
165 : :
8403 bruce@momjian.us 166 : 10766 : funcctx = SRF_PERCALL_SETUP();
8407 tgl@sss.pgh.pa.us 167 : 10766 : mystatus = (PG_Lock_Status *) funcctx->user_fctx;
168 : 10766 : lockData = mystatus->lockData;
169 : :
170 [ + + ]: 20721 : while (mystatus->currIdx < lockData->nelements)
171 : : {
172 : : bool granted;
7680 173 : 20489 : LOCKMODE mode = 0;
174 : : const char *locktypename;
175 : : char tnbuf[32];
1148 peter@eisentraut.org 176 : 20489 : Datum values[NUM_LOCK_STATUS_COLUMNS] = {0};
177 : 20489 : bool nulls[NUM_LOCK_STATUS_COLUMNS] = {0};
178 : : HeapTuple tuple;
179 : : Datum result;
180 : : LockInstanceData *instance;
181 : :
5215 rhaas@postgresql.org 182 : 20489 : 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 : : */
8407 tgl@sss.pgh.pa.us 189 : 20489 : granted = false;
5215 rhaas@postgresql.org 190 [ + + ]: 20489 : if (instance->holdMask)
191 : : {
7680 tgl@sss.pgh.pa.us 192 [ + - ]: 49921 : for (mode = 0; mode < MAX_LOCKMODES; mode++)
193 : : {
5215 rhaas@postgresql.org 194 [ + + ]: 49921 : if (instance->holdMask & LOCKBIT_ON(mode))
195 : : {
7680 tgl@sss.pgh.pa.us 196 : 10525 : granted = true;
5215 rhaas@postgresql.org 197 : 10525 : instance->holdMask &= LOCKBIT_OFF(mode);
7680 tgl@sss.pgh.pa.us 198 : 10525 : 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 : : */
8407 207 [ + + ]: 20489 : if (!granted)
208 : : {
5215 rhaas@postgresql.org 209 [ + + ]: 9964 : if (instance->waitLockMode != NoLock)
210 : : {
211 : : /* Yes, so report it with proper mode */
212 : 9 : 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 : : */
8407 tgl@sss.pgh.pa.us 218 : 9 : 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 : 9955 : mystatus->currIdx++;
227 : 9955 : continue;
228 : : }
229 : : }
230 : :
231 : : /*
232 : : * Form tuple with appropriate data.
233 : : */
234 : :
5215 rhaas@postgresql.org 235 [ + - ]: 10534 : if (instance->locktag.locktag_type <= LOCKTAG_LAST_TYPE)
236 : 10534 : locktypename = LockTagTypeNames[instance->locktag.locktag_type];
237 : : else
238 : : {
7417 tgl@sss.pgh.pa.us 239 :UBC 0 : snprintf(tnbuf, sizeof(tnbuf), "unknown %d",
5215 rhaas@postgresql.org 240 : 0 : (int) instance->locktag.locktag_type);
7417 tgl@sss.pgh.pa.us 241 : 0 : locktypename = tnbuf;
242 : : }
6374 tgl@sss.pgh.pa.us 243 :CBC 10534 : values[0] = CStringGetTextDatum(locktypename);
244 : :
5215 rhaas@postgresql.org 245 [ + - - + : 10534 : switch ((LockTagType) instance->locktag.locktag_type)
+ + + -
+ ]
246 : : {
7435 tgl@sss.pgh.pa.us 247 : 7541 : case LOCKTAG_RELATION:
248 : : case LOCKTAG_RELATION_EXTEND:
5215 rhaas@postgresql.org 249 : 7541 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
250 : 7541 : values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
6152 tgl@sss.pgh.pa.us 251 : 7541 : nulls[3] = true;
252 : 7541 : nulls[4] = true;
253 : 7541 : nulls[5] = true;
254 : 7541 : nulls[6] = true;
255 : 7541 : nulls[7] = true;
256 : 7541 : nulls[8] = true;
257 : 7541 : nulls[9] = true;
7417 258 : 7541 : break;
1848 noah@leadboat.com 259 :UBC 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;
7435 tgl@sss.pgh.pa.us 270 : 0 : case LOCKTAG_PAGE:
5215 rhaas@postgresql.org 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);
6152 tgl@sss.pgh.pa.us 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;
7417 280 : 0 : break;
7435 tgl@sss.pgh.pa.us 281 :CBC 3 : case LOCKTAG_TUPLE:
5215 rhaas@postgresql.org 282 : 3 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
283 : 3 : values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
284 : 3 : values[3] = UInt32GetDatum(instance->locktag.locktag_field3);
285 : 3 : values[4] = UInt16GetDatum(instance->locktag.locktag_field4);
6152 tgl@sss.pgh.pa.us 286 : 3 : nulls[5] = true;
287 : 3 : nulls[6] = true;
288 : 3 : nulls[7] = true;
289 : 3 : nulls[8] = true;
290 : 3 : nulls[9] = true;
7435 291 : 3 : break;
292 : 831 : case LOCKTAG_TRANSACTION:
5215 rhaas@postgresql.org 293 : 831 : values[6] =
294 : 831 : TransactionIdGetDatum(instance->locktag.locktag_field1);
6152 tgl@sss.pgh.pa.us 295 : 831 : nulls[1] = true;
296 : 831 : nulls[2] = true;
297 : 831 : nulls[3] = true;
298 : 831 : nulls[4] = true;
299 : 831 : nulls[5] = true;
300 : 831 : nulls[7] = true;
301 : 831 : nulls[8] = true;
302 : 831 : nulls[9] = true;
6576 303 : 831 : break;
304 : 1389 : case LOCKTAG_VIRTUALTRANSACTION:
5215 rhaas@postgresql.org 305 : 1389 : values[5] = VXIDGetDatum(instance->locktag.locktag_field1,
306 : : instance->locktag.locktag_field2);
6152 tgl@sss.pgh.pa.us 307 : 1389 : nulls[1] = true;
308 : 1389 : nulls[2] = true;
309 : 1389 : nulls[3] = true;
310 : 1389 : nulls[4] = true;
311 : 1389 : nulls[6] = true;
312 : 1389 : nulls[7] = true;
313 : 1389 : nulls[8] = true;
314 : 1389 : nulls[9] = true;
7417 315 : 1389 : break;
970 akapila@postgresql.o 316 : 3 : case LOCKTAG_SPECULATIVE_TOKEN:
317 : 3 : values[6] =
318 : 3 : TransactionIdGetDatum(instance->locktag.locktag_field1);
319 : 3 : values[8] = ObjectIdGetDatum(instance->locktag.locktag_field2);
320 : 3 : nulls[1] = true;
321 : 3 : nulls[2] = true;
322 : 3 : nulls[3] = true;
323 : 3 : nulls[4] = true;
324 : 3 : nulls[5] = true;
325 : 3 : nulls[7] = true;
326 : 3 : nulls[9] = true;
327 : 3 : break;
971 akapila@postgresql.o 328 :UBC 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;
7417 tgl@sss.pgh.pa.us 339 :CBC 767 : case LOCKTAG_OBJECT:
340 : : case LOCKTAG_USERLOCK:
341 : : case LOCKTAG_ADVISORY:
342 : : default: /* treat unknown locktags like OBJECT */
5215 rhaas@postgresql.org 343 : 767 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
344 : 767 : values[7] = ObjectIdGetDatum(instance->locktag.locktag_field2);
345 : 767 : values[8] = ObjectIdGetDatum(instance->locktag.locktag_field3);
346 : 767 : values[9] = Int16GetDatum(instance->locktag.locktag_field4);
6152 tgl@sss.pgh.pa.us 347 : 767 : nulls[2] = true;
348 : 767 : nulls[3] = true;
349 : 767 : nulls[4] = true;
350 : 767 : nulls[5] = true;
351 : 767 : nulls[6] = true;
7435 352 : 767 : break;
353 : : }
354 : :
552 heikki.linnakangas@i 355 : 10534 : values[10] = VXIDGetDatum(instance->vxid.procNumber, instance->vxid.localTransactionId);
5215 rhaas@postgresql.org 356 [ + + ]: 10534 : if (instance->pid != 0)
357 : 10512 : values[11] = Int32GetDatum(instance->pid);
358 : : else
6152 tgl@sss.pgh.pa.us 359 : 22 : nulls[11] = true;
5215 rhaas@postgresql.org 360 : 10534 : values[12] = CStringGetTextDatum(GetLockmodeName(instance->locktag.locktag_lockmethodid, mode));
6576 tgl@sss.pgh.pa.us 361 : 10534 : values[13] = BoolGetDatum(granted);
5215 rhaas@postgresql.org 362 : 10534 : values[14] = BoolGetDatum(instance->fastpath);
1664 fujii@postgresql.org 363 [ + + + - ]: 10534 : if (!granted && instance->waitStart != 0)
364 : 9 : values[15] = TimestampTzGetDatum(instance->waitStart);
365 : : else
366 : 10525 : nulls[15] = true;
367 : :
6152 tgl@sss.pgh.pa.us 368 : 10534 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
7828 369 : 10534 : result = HeapTupleGetDatum(tuple);
8409 370 : 10534 : SRF_RETURN_NEXT(funcctx, result);
371 : : }
372 : :
373 : : /*
374 : : * Have returned all regular locks. Now start on the SIREAD predicate
375 : : * locks.
376 : : */
5325 heikki.linnakangas@i 377 : 232 : predLockData = mystatus->predLockData;
378 [ + + ]: 232 : if (mystatus->predLockIdx < predLockData->nelements)
379 : : {
380 : : PredicateLockTargetType lockType;
381 : :
382 : 3 : PREDICATELOCKTARGETTAG *predTag = &(predLockData->locktags[mystatus->predLockIdx]);
383 : 3 : SERIALIZABLEXACT *xact = &(predLockData->xacts[mystatus->predLockIdx]);
1148 peter@eisentraut.org 384 : 3 : Datum values[NUM_LOCK_STATUS_COLUMNS] = {0};
385 : 3 : bool nulls[NUM_LOCK_STATUS_COLUMNS] = {0};
386 : : HeapTuple tuple;
387 : : Datum result;
388 : :
5325 heikki.linnakangas@i 389 : 3 : mystatus->predLockIdx++;
390 : :
391 : : /*
392 : : * Form tuple with appropriate data.
393 : : */
394 : :
395 : : /* lock type */
396 [ - + ]: 3 : lockType = GET_PREDICATELOCKTARGETTAG_TYPE(*predTag);
397 : :
398 : 3 : values[0] = CStringGetTextDatum(PredicateLockTagTypeNames[lockType]);
399 : :
400 : : /* lock target */
29 peter@eisentraut.org 401 :GNC 3 : values[1] = ObjectIdGetDatum(GET_PREDICATELOCKTARGETTAG_DB(*predTag));
402 : 3 : values[2] = ObjectIdGetDatum(GET_PREDICATELOCKTARGETTAG_RELATION(*predTag));
5325 heikki.linnakangas@i 403 [ + - ]:CBC 3 : if (lockType == PREDLOCKTAG_TUPLE)
29 peter@eisentraut.org 404 :GNC 3 : values[4] = UInt16GetDatum(GET_PREDICATELOCKTARGETTAG_OFFSET(*predTag));
405 : : else
5325 heikki.linnakangas@i 406 :UBC 0 : nulls[4] = true;
5325 heikki.linnakangas@i 407 [ - + - - ]:CBC 3 : if ((lockType == PREDLOCKTAG_TUPLE) ||
408 : : (lockType == PREDLOCKTAG_PAGE))
29 peter@eisentraut.org 409 :GNC 3 : values[3] = UInt32GetDatum(GET_PREDICATELOCKTARGETTAG_PAGE(*predTag));
410 : : else
5325 heikki.linnakangas@i 411 :UBC 0 : nulls[3] = true;
412 : :
413 : : /* these fields are targets for other types of locks */
5325 heikki.linnakangas@i 414 :CBC 3 : nulls[5] = true; /* virtualxid */
415 : 3 : nulls[6] = true; /* transactionid */
416 : 3 : nulls[7] = true; /* classid */
417 : 3 : nulls[8] = true; /* objid */
418 : 3 : nulls[9] = true; /* objsubid */
419 : :
420 : : /* lock holder */
552 421 : 3 : values[10] = VXIDGetDatum(xact->vxid.procNumber,
422 : : xact->vxid.localTransactionId);
5269 rhaas@postgresql.org 423 [ + - ]: 3 : if (xact->pid != 0)
424 : 3 : values[11] = Int32GetDatum(xact->pid);
425 : : else
5269 rhaas@postgresql.org 426 :UBC 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 : : */
5325 heikki.linnakangas@i 432 :CBC 3 : values[12] = CStringGetTextDatum("SIReadLock");
433 : 3 : values[13] = BoolGetDatum(true);
5215 rhaas@postgresql.org 434 : 3 : values[14] = BoolGetDatum(false);
1664 fujii@postgresql.org 435 : 3 : nulls[15] = true;
436 : :
5325 heikki.linnakangas@i 437 : 3 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
438 : 3 : result = HeapTupleGetDatum(tuple);
439 : 3 : SRF_RETURN_NEXT(funcctx, result);
440 : : }
441 : :
8409 tgl@sss.pgh.pa.us 442 : 229 : 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
3484 466 : 1920 : pg_blocking_pids(PG_FUNCTION_ARGS)
467 : : {
468 : 1920 : 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 : 1920 : lockData = GetBlockerStatusData(blocked_pid);
477 : :
478 : : /* We can't need more output entries than there are reported PROCLOCKs */
479 : 1920 : arrayelems = (Datum *) palloc(lockData->nlocks * sizeof(Datum));
480 : 1920 : narrayelems = 0;
481 : :
482 : : /* For each blocked proc in the lock group ... */
483 [ + + ]: 3055 : for (i = 0; i < lockData->nprocs; i++)
484 : : {
485 : 1135 : BlockedProcData *bproc = &lockData->procs[i];
486 : 1135 : LockInstanceData *instances = &lockData->locks[bproc->first_lock];
487 : 1135 : 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 : 1135 : blocked_instance = NULL;
497 [ + + ]: 3455 : for (j = 0; j < bproc->num_locks; j++)
498 : : {
499 : 2320 : LockInstanceData *instance = &(instances[j]);
500 : :
501 [ + + ]: 2320 : if (instance->pid == bproc->pid)
502 : : {
503 [ - + ]: 1135 : Assert(blocked_instance == NULL);
504 : 1135 : blocked_instance = instance;
505 : : }
506 : : }
507 [ - + ]: 1135 : Assert(blocked_instance != NULL);
508 : :
509 : 1135 : lockMethodTable = GetLockTagsMethodTable(&(blocked_instance->locktag));
510 : 1135 : conflictMask = lockMethodTable->conflictTab[blocked_instance->waitLockMode];
511 : :
512 : : /* Now scan the PROCLOCK data for conflicting procs */
513 [ + + ]: 3455 : for (j = 0; j < bproc->num_locks; j++)
514 : : {
515 : 2320 : LockInstanceData *instance = &(instances[j]);
516 : :
517 : : /* A proc never blocks itself, so ignore that entry */
518 [ + + ]: 2320 : if (instance == blocked_instance)
519 : 1135 : continue;
520 : : /* Members of same lock group never block each other, either */
521 [ + + ]: 1185 : if (instance->leaderPid == blocked_instance->leaderPid)
522 : 4 : continue;
523 : :
524 [ + + ]: 1181 : if (conflictMask & instance->holdMask)
525 : : {
526 : : /* hard block: blocked by lock already held by this entry */
527 : : }
528 [ + + ]: 47 : else if (instance->waitLockMode != NoLock &&
529 [ + + ]: 41 : (conflictMask & LOCKBIT_ON(instance->waitLockMode)))
530 : 10 : {
531 : : /* conflict in lock requests; who's in front in wait queue? */
532 : 21 : bool ahead = false;
533 : : int k;
534 : :
535 [ + + ]: 21 : for (k = 0; k < bproc->num_waiters; k++)
536 : : {
537 [ + - ]: 10 : if (preceding_waiters[k] == instance->pid)
538 : : {
539 : : /* soft block: this entry is ahead of blocked proc */
540 : 10 : ahead = true;
541 : 10 : break;
542 : : }
543 : : }
544 [ + + ]: 21 : if (!ahead)
545 : 11 : continue; /* not blocked by this entry */
546 : : }
547 : : else
548 : : {
549 : : /* not blocked by this entry */
550 : 26 : continue;
551 : : }
552 : :
553 : : /* blocked by this entry, so emit a record */
554 : 1144 : arrayelems[narrayelems++] = Int32GetDatum(instance->leaderPid);
555 : : }
556 : : }
557 : :
558 : : /* Assert we didn't overrun arrayelems[] */
559 [ - + ]: 1920 : Assert(narrayelems <= lockData->nlocks);
560 : :
1163 peter@eisentraut.org 561 : 1920 : 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
3071 tgl@sss.pgh.pa.us 573 :UBC 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 */
1243 rhaas@postgresql.org 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 */
3071 tgl@sss.pgh.pa.us 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 : :
1163 peter@eisentraut.org 599 : 0 : PG_RETURN_ARRAYTYPE_P(construct_array_builtin(blocker_datums, num_blockers, INT4OID));
600 : : }
601 : :
602 : :
603 : : /*
604 : : * Functions for manipulating advisory locks
605 : : *
606 : : * We make use of the locktag fields as follows:
607 : : *
608 : : * field1: MyDatabaseId ... ensures locks are local to each database
609 : : * field2: first of 2 int4 keys, or high-order half of an int8 key
610 : : * field3: second of 2 int4 keys, or low-order half of an int8 key
611 : : * field4: 1 if using an int8 key, 2 if using 2 int4 keys
612 : : */
613 : : #define SET_LOCKTAG_INT64(tag, key64) \
614 : : SET_LOCKTAG_ADVISORY(tag, \
615 : : MyDatabaseId, \
616 : : (uint32) ((key64) >> 32), \
617 : : (uint32) (key64), \
618 : : 1)
619 : : #define SET_LOCKTAG_INT32(tag, key1, key2) \
620 : : SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, key1, key2, 2)
621 : :
622 : : /*
623 : : * pg_advisory_lock(int8) - acquire exclusive lock on an int8 key
624 : : */
625 : : Datum
6928 tgl@sss.pgh.pa.us 626 :CBC 212 : pg_advisory_lock_int8(PG_FUNCTION_ARGS)
627 : : {
628 : 212 : int64 key = PG_GETARG_INT64(0);
629 : : LOCKTAG tag;
630 : :
631 : 212 : SET_LOCKTAG_INT64(tag, key);
632 : :
633 : 212 : (void) LockAcquire(&tag, ExclusiveLock, true, false);
634 : :
635 : 211 : PG_RETURN_VOID();
636 : : }
637 : :
638 : : /*
639 : : * pg_advisory_xact_lock(int8) - acquire xact scoped
640 : : * exclusive lock on an int8 key
641 : : */
642 : : Datum
5314 itagaki.takahiro@gma 643 : 256 : pg_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
644 : : {
645 : 256 : int64 key = PG_GETARG_INT64(0);
646 : : LOCKTAG tag;
647 : :
648 : 256 : SET_LOCKTAG_INT64(tag, key);
649 : :
650 : 256 : (void) LockAcquire(&tag, ExclusiveLock, false, false);
651 : :
652 : 256 : PG_RETURN_VOID();
653 : : }
654 : :
655 : : /*
656 : : * pg_advisory_lock_shared(int8) - acquire share lock on an int8 key
657 : : */
658 : : Datum
6928 tgl@sss.pgh.pa.us 659 : 28 : pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
660 : : {
661 : 28 : int64 key = PG_GETARG_INT64(0);
662 : : LOCKTAG tag;
663 : :
664 : 28 : SET_LOCKTAG_INT64(tag, key);
665 : :
666 : 28 : (void) LockAcquire(&tag, ShareLock, true, false);
667 : :
668 : 28 : PG_RETURN_VOID();
669 : : }
670 : :
671 : : /*
672 : : * pg_advisory_xact_lock_shared(int8) - acquire xact scoped
673 : : * share lock on an int8 key
674 : : */
675 : : Datum
5314 itagaki.takahiro@gma 676 : 20021 : pg_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
677 : : {
678 : 20021 : int64 key = PG_GETARG_INT64(0);
679 : : LOCKTAG tag;
680 : :
681 : 20021 : SET_LOCKTAG_INT64(tag, key);
682 : :
683 : 20021 : (void) LockAcquire(&tag, ShareLock, false, false);
684 : :
685 : 20021 : PG_RETURN_VOID();
686 : : }
687 : :
688 : : /*
689 : : * pg_try_advisory_lock(int8) - acquire exclusive lock on an int8 key, no wait
690 : : *
691 : : * Returns true if successful, false if lock not available
692 : : */
693 : : Datum
6928 tgl@sss.pgh.pa.us 694 : 512 : pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
695 : : {
696 : 512 : int64 key = PG_GETARG_INT64(0);
697 : : LOCKTAG tag;
698 : : LockAcquireResult res;
699 : :
700 : 512 : SET_LOCKTAG_INT64(tag, key);
701 : :
702 : 512 : res = LockAcquire(&tag, ExclusiveLock, true, true);
703 : :
704 : 512 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
705 : : }
706 : :
707 : : /*
708 : : * pg_try_advisory_xact_lock(int8) - acquire xact scoped
709 : : * exclusive lock on an int8 key, no wait
710 : : *
711 : : * Returns true if successful, false if lock not available
712 : : */
713 : : Datum
5314 itagaki.takahiro@gma 714 :UBC 0 : pg_try_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
715 : : {
716 : 0 : int64 key = PG_GETARG_INT64(0);
717 : : LOCKTAG tag;
718 : : LockAcquireResult res;
719 : :
720 : 0 : SET_LOCKTAG_INT64(tag, key);
721 : :
722 : 0 : res = LockAcquire(&tag, ExclusiveLock, false, true);
723 : :
724 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
725 : : }
726 : :
727 : : /*
728 : : * pg_try_advisory_lock_shared(int8) - acquire share lock on an int8 key, no wait
729 : : *
730 : : * Returns true if successful, false if lock not available
731 : : */
732 : : Datum
6928 tgl@sss.pgh.pa.us 733 : 0 : pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
734 : : {
735 : 0 : int64 key = PG_GETARG_INT64(0);
736 : : LOCKTAG tag;
737 : : LockAcquireResult res;
738 : :
739 : 0 : SET_LOCKTAG_INT64(tag, key);
740 : :
741 : 0 : res = LockAcquire(&tag, ShareLock, true, true);
742 : :
743 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
744 : : }
745 : :
746 : : /*
747 : : * pg_try_advisory_xact_lock_shared(int8) - acquire xact scoped
748 : : * share lock on an int8 key, no wait
749 : : *
750 : : * Returns true if successful, false if lock not available
751 : : */
752 : : Datum
5314 itagaki.takahiro@gma 753 : 0 : pg_try_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
754 : : {
755 : 0 : int64 key = PG_GETARG_INT64(0);
756 : : LOCKTAG tag;
757 : : LockAcquireResult res;
758 : :
759 : 0 : SET_LOCKTAG_INT64(tag, key);
760 : :
761 : 0 : res = LockAcquire(&tag, ShareLock, false, true);
762 : :
763 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
764 : : }
765 : :
766 : : /*
767 : : * pg_advisory_unlock(int8) - release exclusive lock on an int8 key
768 : : *
769 : : * Returns true if successful, false if lock was not held
770 : : */
771 : : Datum
6928 tgl@sss.pgh.pa.us 772 :CBC 134 : pg_advisory_unlock_int8(PG_FUNCTION_ARGS)
773 : : {
774 : 134 : int64 key = PG_GETARG_INT64(0);
775 : : LOCKTAG tag;
776 : : bool res;
777 : :
778 : 134 : SET_LOCKTAG_INT64(tag, key);
779 : :
780 : 134 : res = LockRelease(&tag, ExclusiveLock, true);
781 : :
782 : 134 : PG_RETURN_BOOL(res);
783 : : }
784 : :
785 : : /*
786 : : * pg_advisory_unlock_shared(int8) - release share lock on an int8 key
787 : : *
788 : : * Returns true if successful, false if lock was not held
789 : : */
790 : : Datum
791 : 15 : pg_advisory_unlock_shared_int8(PG_FUNCTION_ARGS)
792 : : {
793 : 15 : int64 key = PG_GETARG_INT64(0);
794 : : LOCKTAG tag;
795 : : bool res;
796 : :
797 : 15 : SET_LOCKTAG_INT64(tag, key);
798 : :
799 : 15 : res = LockRelease(&tag, ShareLock, true);
800 : :
801 : 15 : PG_RETURN_BOOL(res);
802 : : }
803 : :
804 : : /*
805 : : * pg_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys
806 : : */
807 : : Datum
808 : 49 : pg_advisory_lock_int4(PG_FUNCTION_ARGS)
809 : : {
810 : 49 : int32 key1 = PG_GETARG_INT32(0);
811 : 49 : int32 key2 = PG_GETARG_INT32(1);
812 : : LOCKTAG tag;
813 : :
814 : 49 : SET_LOCKTAG_INT32(tag, key1, key2);
815 : :
816 : 49 : (void) LockAcquire(&tag, ExclusiveLock, true, false);
817 : :
818 : 49 : PG_RETURN_VOID();
819 : : }
820 : :
821 : : /*
822 : : * pg_advisory_xact_lock(int4, int4) - acquire xact scoped
823 : : * exclusive lock on 2 int4 keys
824 : : */
825 : : Datum
5314 itagaki.takahiro@gma 826 : 50 : pg_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
827 : : {
828 : 50 : int32 key1 = PG_GETARG_INT32(0);
829 : 50 : int32 key2 = PG_GETARG_INT32(1);
830 : : LOCKTAG tag;
831 : :
832 : 50 : SET_LOCKTAG_INT32(tag, key1, key2);
833 : :
834 : 50 : (void) LockAcquire(&tag, ExclusiveLock, false, false);
835 : :
836 : 50 : PG_RETURN_VOID();
837 : : }
838 : :
839 : : /*
840 : : * pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys
841 : : */
842 : : Datum
6928 tgl@sss.pgh.pa.us 843 : 18 : pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
844 : : {
845 : 18 : int32 key1 = PG_GETARG_INT32(0);
846 : 18 : int32 key2 = PG_GETARG_INT32(1);
847 : : LOCKTAG tag;
848 : :
849 : 18 : SET_LOCKTAG_INT32(tag, key1, key2);
850 : :
851 : 18 : (void) LockAcquire(&tag, ShareLock, true, false);
852 : :
853 : 18 : PG_RETURN_VOID();
854 : : }
855 : :
856 : : /*
857 : : * pg_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
858 : : * share lock on 2 int4 keys
859 : : */
860 : : Datum
5314 itagaki.takahiro@gma 861 : 15 : pg_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
862 : : {
863 : 15 : int32 key1 = PG_GETARG_INT32(0);
864 : 15 : int32 key2 = PG_GETARG_INT32(1);
865 : : LOCKTAG tag;
866 : :
867 : 15 : SET_LOCKTAG_INT32(tag, key1, key2);
868 : :
869 : 15 : (void) LockAcquire(&tag, ShareLock, false, false);
870 : :
871 : 15 : PG_RETURN_VOID();
872 : : }
873 : :
874 : : /*
875 : : * pg_try_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys, no wait
876 : : *
877 : : * Returns true if successful, false if lock not available
878 : : */
879 : : Datum
6928 tgl@sss.pgh.pa.us 880 :UBC 0 : pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
881 : : {
882 : 0 : int32 key1 = PG_GETARG_INT32(0);
883 : 0 : int32 key2 = PG_GETARG_INT32(1);
884 : : LOCKTAG tag;
885 : : LockAcquireResult res;
886 : :
887 : 0 : SET_LOCKTAG_INT32(tag, key1, key2);
888 : :
889 : 0 : res = LockAcquire(&tag, ExclusiveLock, true, true);
890 : :
891 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
892 : : }
893 : :
894 : : /*
895 : : * pg_try_advisory_xact_lock(int4, int4) - acquire xact scoped
896 : : * exclusive lock on 2 int4 keys, no wait
897 : : *
898 : : * Returns true if successful, false if lock not available
899 : : */
900 : : Datum
5314 itagaki.takahiro@gma 901 :CBC 33 : pg_try_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
902 : : {
903 : 33 : int32 key1 = PG_GETARG_INT32(0);
904 : 33 : int32 key2 = PG_GETARG_INT32(1);
905 : : LOCKTAG tag;
906 : : LockAcquireResult res;
907 : :
908 : 33 : SET_LOCKTAG_INT32(tag, key1, key2);
909 : :
910 : 33 : res = LockAcquire(&tag, ExclusiveLock, false, true);
911 : :
912 : 33 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
913 : : }
914 : :
915 : : /*
916 : : * pg_try_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys, no wait
917 : : *
918 : : * Returns true if successful, false if lock not available
919 : : */
920 : : Datum
6928 tgl@sss.pgh.pa.us 921 :UBC 0 : pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
922 : : {
923 : 0 : int32 key1 = PG_GETARG_INT32(0);
924 : 0 : int32 key2 = PG_GETARG_INT32(1);
925 : : LOCKTAG tag;
926 : : LockAcquireResult res;
927 : :
928 : 0 : SET_LOCKTAG_INT32(tag, key1, key2);
929 : :
930 : 0 : res = LockAcquire(&tag, ShareLock, true, true);
931 : :
932 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
933 : : }
934 : :
935 : : /*
936 : : * pg_try_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
937 : : * share lock on 2 int4 keys, no wait
938 : : *
939 : : * Returns true if successful, false if lock not available
940 : : */
941 : : Datum
5314 itagaki.takahiro@gma 942 : 0 : pg_try_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
943 : : {
944 : 0 : int32 key1 = PG_GETARG_INT32(0);
945 : 0 : int32 key2 = PG_GETARG_INT32(1);
946 : : LOCKTAG tag;
947 : : LockAcquireResult res;
948 : :
949 : 0 : SET_LOCKTAG_INT32(tag, key1, key2);
950 : :
951 : 0 : res = LockAcquire(&tag, ShareLock, false, true);
952 : :
953 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
954 : : }
955 : :
956 : : /*
957 : : * pg_advisory_unlock(int4, int4) - release exclusive lock on 2 int4 keys
958 : : *
959 : : * Returns true if successful, false if lock was not held
960 : : */
961 : : Datum
6928 tgl@sss.pgh.pa.us 962 :CBC 47 : pg_advisory_unlock_int4(PG_FUNCTION_ARGS)
963 : : {
964 : 47 : int32 key1 = PG_GETARG_INT32(0);
965 : 47 : int32 key2 = PG_GETARG_INT32(1);
966 : : LOCKTAG tag;
967 : : bool res;
968 : :
969 : 47 : SET_LOCKTAG_INT32(tag, key1, key2);
970 : :
971 : 47 : res = LockRelease(&tag, ExclusiveLock, true);
972 : :
973 : 47 : PG_RETURN_BOOL(res);
974 : : }
975 : :
976 : : /*
977 : : * pg_advisory_unlock_shared(int4, int4) - release share lock on 2 int4 keys
978 : : *
979 : : * Returns true if successful, false if lock was not held
980 : : */
981 : : Datum
982 : 15 : pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
983 : : {
984 : 15 : int32 key1 = PG_GETARG_INT32(0);
985 : 15 : int32 key2 = PG_GETARG_INT32(1);
986 : : LOCKTAG tag;
987 : : bool res;
988 : :
989 : 15 : SET_LOCKTAG_INT32(tag, key1, key2);
990 : :
991 : 15 : res = LockRelease(&tag, ShareLock, true);
992 : :
993 : 15 : PG_RETURN_BOOL(res);
994 : : }
995 : :
996 : : /*
997 : : * pg_advisory_unlock_all() - release all advisory locks
998 : : */
999 : : Datum
1000 : 119 : pg_advisory_unlock_all(PG_FUNCTION_ARGS)
1001 : : {
5314 itagaki.takahiro@gma 1002 : 119 : LockReleaseSession(USER_LOCKMETHOD);
1003 : :
6928 tgl@sss.pgh.pa.us 1004 : 119 : PG_RETURN_VOID();
1005 : : }
|