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-2026, 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
953 heikki.linnakangas@i 74 :CBC 14657 : 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 : 14657 : snprintf(vxidstr, sizeof(vxidstr), "%d/%u", procNumber, lxid);
84 : :
6775 tgl@sss.pgh.pa.us 85 : 14657 : 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
8812 bruce@momjian.us 93 : 13157 : pg_lock_status(PG_FUNCTION_ARGS)
94 : : {
95 : : FuncCallContext *funcctx;
96 : : PG_Lock_Status *mystatus;
97 : : LockData *lockData;
98 : : PredicateLockData *predLockData;
99 : :
8822 100 [ + + ]: 13157 : if (SRF_IS_FIRSTCALL())
101 : : {
102 : : TupleDesc tupdesc;
103 : : MemoryContext oldcontext;
104 : :
105 : : /* create a function context for cross-call persistence */
8810 tgl@sss.pgh.pa.us 106 : 311 : funcctx = SRF_FIRSTCALL_INIT();
107 : :
108 : : /*
109 : : * switch to memory context appropriate for multiple function calls
110 : : */
111 : 311 : 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 */
2883 andres@anarazel.de 115 : 311 : tupdesc = CreateTemplateTupleDesc(NUM_LOCK_STATUS_COLUMNS);
7818 tgl@sss.pgh.pa.us 116 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype",
117 : : TEXTOID, -1, 0);
8636 bruce@momjian.us 118 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
119 : : OIDOID, -1, 0);
7818 tgl@sss.pgh.pa.us 120 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relation",
121 : : OIDOID, -1, 0);
122 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 4, "page",
123 : : INT4OID, -1, 0);
124 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 5, "tuple",
125 : : INT2OID, -1, 0);
6977 126 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 6, "virtualxid",
127 : : TEXTOID, -1, 0);
128 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 7, "transactionid",
129 : : XIDOID, -1, 0);
130 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 8, "classid",
131 : : OIDOID, -1, 0);
132 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objid",
133 : : OIDOID, -1, 0);
134 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 10, "objsubid",
135 : : INT2OID, -1, 0);
136 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 11, "virtualtransaction",
137 : : TEXTOID, -1, 0);
138 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 12, "pid",
139 : : INT4OID, -1, 0);
140 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 13, "mode",
141 : : TEXTOID, -1, 0);
142 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 14, "granted",
143 : : BOOLOID, -1, 0);
5616 rhaas@postgresql.org 144 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 15, "fastpath",
145 : : BOOLOID, -1, 0);
2065 fujii@postgresql.org 146 : 311 : TupleDescInitEntry(tupdesc, (AttrNumber) 16, "waitstart",
147 : : TIMESTAMPTZOID, -1, 0);
148 : :
210 drowley@postgresql.o 149 : 311 : TupleDescFinalize(tupdesc);
8229 tgl@sss.pgh.pa.us 150 : 311 : funcctx->tuple_desc = BlessTupleDesc(tupdesc);
151 : :
152 : : /*
153 : : * Collect all the locking information that we will format and send
154 : : * out as a result set.
155 : : */
306 michael@paquier.xyz 156 : 311 : mystatus = palloc_object(PG_Lock_Status);
683 peter@eisentraut.org 157 : 311 : funcctx->user_fctx = mystatus;
158 : :
8808 tgl@sss.pgh.pa.us 159 : 311 : mystatus->lockData = GetLockStatusData();
160 : 311 : mystatus->currIdx = 0;
5726 heikki.linnakangas@i 161 : 311 : mystatus->predLockData = GetPredicateLockStatusData();
162 : 311 : mystatus->predLockIdx = 0;
163 : :
8810 tgl@sss.pgh.pa.us 164 : 311 : MemoryContextSwitchTo(oldcontext);
165 : : }
166 : :
8804 bruce@momjian.us 167 : 13157 : funcctx = SRF_PERCALL_SETUP();
8808 tgl@sss.pgh.pa.us 168 : 13157 : mystatus = (PG_Lock_Status *) funcctx->user_fctx;
169 : 13157 : lockData = mystatus->lockData;
170 : :
171 [ + + ]: 25485 : while (mystatus->currIdx < lockData->nelements)
172 : : {
173 : : bool granted;
8081 174 : 25170 : LOCKMODE mode = 0;
175 : : const char *locktypename;
176 : : char tnbuf[32];
1549 peter@eisentraut.org 177 : 25170 : Datum values[NUM_LOCK_STATUS_COLUMNS] = {0};
178 : 25170 : bool nulls[NUM_LOCK_STATUS_COLUMNS] = {0};
179 : : HeapTuple tuple;
180 : : Datum result;
181 : : LockInstanceData *instance;
182 : :
5616 rhaas@postgresql.org 183 : 25170 : instance = &(lockData->locks[mystatus->currIdx]);
184 : :
185 : : /*
186 : : * Look to see if there are any held lock modes in this PROCLOCK. If
187 : : * so, report, and destructively modify lockData so we don't report
188 : : * again.
189 : : */
8808 tgl@sss.pgh.pa.us 190 : 25170 : granted = false;
5616 rhaas@postgresql.org 191 [ + + ]: 25170 : if (instance->holdMask)
192 : : {
8081 tgl@sss.pgh.pa.us 193 [ + - ]: 59637 : for (mode = 0; mode < MAX_LOCKMODES; mode++)
194 : : {
5616 rhaas@postgresql.org 195 [ + + ]: 59637 : if (instance->holdMask & LOCKBIT_ON(mode))
196 : : {
8081 tgl@sss.pgh.pa.us 197 : 12828 : granted = true;
5616 rhaas@postgresql.org 198 : 12828 : instance->holdMask &= LOCKBIT_OFF(mode);
8081 tgl@sss.pgh.pa.us 199 : 12828 : break;
200 : : }
201 : : }
202 : : }
203 : :
204 : : /*
205 : : * If no (more) held modes to report, see if PROC is waiting for a
206 : : * lock on this lock.
207 : : */
8808 208 [ + + ]: 25170 : if (!granted)
209 : : {
5616 rhaas@postgresql.org 210 [ + + ]: 12342 : if (instance->waitLockMode != NoLock)
211 : : {
212 : : /* Yes, so report it with proper mode */
213 : 14 : mode = instance->waitLockMode;
214 : :
215 : : /*
216 : : * We are now done with this PROCLOCK, so advance pointer to
217 : : * continue with next one on next call.
218 : : */
8808 tgl@sss.pgh.pa.us 219 : 14 : mystatus->currIdx++;
220 : : }
221 : : else
222 : : {
223 : : /*
224 : : * Okay, we've displayed all the locks associated with this
225 : : * PROCLOCK, proceed to the next one.
226 : : */
227 : 12328 : mystatus->currIdx++;
228 : 12328 : continue;
229 : : }
230 : : }
231 : :
232 : : /*
233 : : * Form tuple with appropriate data.
234 : : */
235 : :
5616 rhaas@postgresql.org 236 [ + - ]: 12842 : if (instance->locktag.locktag_type <= LOCKTAG_LAST_TYPE)
237 : 12842 : locktypename = LockTagTypeNames[instance->locktag.locktag_type];
238 : : else
239 : : {
7818 tgl@sss.pgh.pa.us 240 :UBC 0 : snprintf(tnbuf, sizeof(tnbuf), "unknown %d",
5616 rhaas@postgresql.org 241 : 0 : (int) instance->locktag.locktag_type);
7818 tgl@sss.pgh.pa.us 242 : 0 : locktypename = tnbuf;
243 : : }
6775 tgl@sss.pgh.pa.us 244 :CBC 12842 : values[0] = CStringGetTextDatum(locktypename);
245 : :
5616 rhaas@postgresql.org 246 [ + + - + : 12842 : switch ((LockTagType) instance->locktag.locktag_type)
+ + + -
+ ]
247 : : {
7836 tgl@sss.pgh.pa.us 248 : 8625 : case LOCKTAG_RELATION:
249 : : case LOCKTAG_RELATION_EXTEND:
5616 rhaas@postgresql.org 250 : 8625 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
251 : 8625 : values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
6553 tgl@sss.pgh.pa.us 252 : 8625 : nulls[3] = true;
253 : 8625 : nulls[4] = true;
254 : 8625 : nulls[5] = true;
255 : 8625 : nulls[6] = true;
256 : 8625 : nulls[7] = true;
257 : 8625 : nulls[8] = true;
258 : 8625 : nulls[9] = true;
7818 259 : 8625 : break;
2249 noah@leadboat.com 260 :GBC 2 : case LOCKTAG_DATABASE_FROZEN_IDS:
261 : 2 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
262 : 2 : nulls[2] = true;
263 : 2 : nulls[3] = true;
264 : 2 : nulls[4] = true;
265 : 2 : nulls[5] = true;
266 : 2 : nulls[6] = true;
267 : 2 : nulls[7] = true;
268 : 2 : nulls[8] = true;
269 : 2 : nulls[9] = true;
270 : 2 : break;
7836 tgl@sss.pgh.pa.us 271 :UBC 0 : case LOCKTAG_PAGE:
5616 rhaas@postgresql.org 272 : 0 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
273 : 0 : values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
96 michael@paquier.xyz 274 :UNC 0 : values[3] = Int32GetDatum(instance->locktag.locktag_field3);
6553 tgl@sss.pgh.pa.us 275 :UBC 0 : nulls[4] = true;
276 : 0 : nulls[5] = true;
277 : 0 : nulls[6] = true;
278 : 0 : nulls[7] = true;
279 : 0 : nulls[8] = true;
280 : 0 : nulls[9] = true;
7818 281 : 0 : break;
7836 tgl@sss.pgh.pa.us 282 :CBC 5 : case LOCKTAG_TUPLE:
5616 rhaas@postgresql.org 283 : 5 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
284 : 5 : values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
96 michael@paquier.xyz 285 :GNC 5 : values[3] = Int32GetDatum(instance->locktag.locktag_field3);
286 : 5 : values[4] = Int16GetDatum(instance->locktag.locktag_field4);
6553 tgl@sss.pgh.pa.us 287 :CBC 5 : nulls[5] = true;
288 : 5 : nulls[6] = true;
289 : 5 : nulls[7] = true;
290 : 5 : nulls[8] = true;
291 : 5 : nulls[9] = true;
7836 292 : 5 : break;
293 : 1012 : case LOCKTAG_TRANSACTION:
5616 rhaas@postgresql.org 294 : 1012 : values[6] =
295 : 1012 : TransactionIdGetDatum(instance->locktag.locktag_field1);
6553 tgl@sss.pgh.pa.us 296 : 1012 : nulls[1] = true;
297 : 1012 : nulls[2] = true;
298 : 1012 : nulls[3] = true;
299 : 1012 : nulls[4] = true;
300 : 1012 : nulls[5] = true;
301 : 1012 : nulls[7] = true;
302 : 1012 : nulls[8] = true;
303 : 1012 : nulls[9] = true;
6977 304 : 1012 : break;
305 : 1811 : case LOCKTAG_VIRTUALTRANSACTION:
5616 rhaas@postgresql.org 306 : 1811 : values[5] = VXIDGetDatum(instance->locktag.locktag_field1,
307 : : instance->locktag.locktag_field2);
6553 tgl@sss.pgh.pa.us 308 : 1811 : nulls[1] = true;
309 : 1811 : nulls[2] = true;
310 : 1811 : nulls[3] = true;
311 : 1811 : nulls[4] = true;
312 : 1811 : nulls[6] = true;
313 : 1811 : nulls[7] = true;
314 : 1811 : nulls[8] = true;
315 : 1811 : nulls[9] = true;
7818 316 : 1811 : break;
1371 akapila@postgresql.o 317 : 2 : case LOCKTAG_SPECULATIVE_TOKEN:
318 : 2 : values[6] =
319 : 2 : TransactionIdGetDatum(instance->locktag.locktag_field1);
320 : 2 : values[8] = ObjectIdGetDatum(instance->locktag.locktag_field2);
321 : 2 : nulls[1] = true;
322 : 2 : nulls[2] = true;
323 : 2 : nulls[3] = true;
324 : 2 : nulls[4] = true;
325 : 2 : nulls[5] = true;
326 : 2 : nulls[7] = true;
327 : 2 : nulls[9] = true;
328 : 2 : break;
1372 akapila@postgresql.o 329 :UBC 0 : case LOCKTAG_APPLY_TRANSACTION:
330 : 0 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
331 : 0 : values[8] = ObjectIdGetDatum(instance->locktag.locktag_field2);
332 : 0 : values[6] = ObjectIdGetDatum(instance->locktag.locktag_field3);
165 michael@paquier.xyz 333 : 0 : values[9] = Int16GetDatum(instance->locktag.locktag_field4);
1372 akapila@postgresql.o 334 : 0 : nulls[2] = true;
335 : 0 : nulls[3] = true;
336 : 0 : nulls[4] = true;
337 : 0 : nulls[5] = true;
338 : 0 : nulls[7] = true;
339 : 0 : break;
7818 tgl@sss.pgh.pa.us 340 :CBC 1385 : case LOCKTAG_OBJECT:
341 : : case LOCKTAG_USERLOCK:
342 : : case LOCKTAG_ADVISORY:
343 : : default: /* treat unknown locktags like OBJECT */
5616 rhaas@postgresql.org 344 : 1385 : values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
345 : 1385 : values[7] = ObjectIdGetDatum(instance->locktag.locktag_field2);
346 : 1385 : values[8] = ObjectIdGetDatum(instance->locktag.locktag_field3);
165 michael@paquier.xyz 347 : 1385 : values[9] = Int16GetDatum(instance->locktag.locktag_field4);
6553 tgl@sss.pgh.pa.us 348 : 1385 : nulls[2] = true;
349 : 1385 : nulls[3] = true;
350 : 1385 : nulls[4] = true;
351 : 1385 : nulls[5] = true;
352 : 1385 : nulls[6] = true;
7836 353 : 1385 : break;
354 : : }
355 : :
953 heikki.linnakangas@i 356 : 12842 : values[10] = VXIDGetDatum(instance->vxid.procNumber, instance->vxid.localTransactionId);
5616 rhaas@postgresql.org 357 [ + + ]: 12842 : if (instance->pid != 0)
358 : 12803 : values[11] = Int32GetDatum(instance->pid);
359 : : else
6553 tgl@sss.pgh.pa.us 360 : 39 : nulls[11] = true;
5616 rhaas@postgresql.org 361 : 12842 : values[12] = CStringGetTextDatum(GetLockmodeName(instance->locktag.locktag_lockmethodid, mode));
6977 tgl@sss.pgh.pa.us 362 : 12842 : values[13] = BoolGetDatum(granted);
5616 rhaas@postgresql.org 363 : 12842 : values[14] = BoolGetDatum(instance->fastpath);
2065 fujii@postgresql.org 364 [ + + + - ]: 12842 : if (!granted && instance->waitStart != 0)
365 : 14 : values[15] = TimestampTzGetDatum(instance->waitStart);
366 : : else
367 : 12828 : nulls[15] = true;
368 : :
6553 tgl@sss.pgh.pa.us 369 : 12842 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
8229 370 : 12842 : result = HeapTupleGetDatum(tuple);
8810 371 : 12842 : SRF_RETURN_NEXT(funcctx, result);
372 : : }
373 : :
374 : : /*
375 : : * Have returned all regular locks. Now start on the SIREAD predicate
376 : : * locks.
377 : : */
5726 heikki.linnakangas@i 378 : 315 : predLockData = mystatus->predLockData;
379 [ + + ]: 315 : if (mystatus->predLockIdx < predLockData->nelements)
380 : : {
381 : : PredicateLockTargetType lockType;
382 : :
383 : 4 : PREDICATELOCKTARGETTAG *predTag = &(predLockData->locktags[mystatus->predLockIdx]);
384 : 4 : SERIALIZABLEXACT *xact = &(predLockData->xacts[mystatus->predLockIdx]);
1549 peter@eisentraut.org 385 : 4 : Datum values[NUM_LOCK_STATUS_COLUMNS] = {0};
386 : 4 : bool nulls[NUM_LOCK_STATUS_COLUMNS] = {0};
387 : : HeapTuple tuple;
388 : : Datum result;
389 : :
5726 heikki.linnakangas@i 390 : 4 : mystatus->predLockIdx++;
391 : :
392 : : /*
393 : : * Form tuple with appropriate data.
394 : : */
395 : :
396 : : /* lock type */
397 [ - + ]: 4 : lockType = GET_PREDICATELOCKTARGETTAG_TYPE(*predTag);
398 : :
399 : 4 : values[0] = CStringGetTextDatum(PredicateLockTagTypeNames[lockType]);
400 : :
401 : : /* lock target */
430 peter@eisentraut.org 402 : 4 : values[1] = ObjectIdGetDatum(GET_PREDICATELOCKTARGETTAG_DB(*predTag));
403 : 4 : values[2] = ObjectIdGetDatum(GET_PREDICATELOCKTARGETTAG_RELATION(*predTag));
5726 heikki.linnakangas@i 404 [ + - ]: 4 : if (lockType == PREDLOCKTAG_TUPLE)
96 michael@paquier.xyz 405 :GNC 4 : values[4] = Int16GetDatum(GET_PREDICATELOCKTARGETTAG_OFFSET(*predTag));
406 : : else
5726 heikki.linnakangas@i 407 :UBC 0 : nulls[4] = true;
5726 heikki.linnakangas@i 408 [ - + - - ]:CBC 4 : if ((lockType == PREDLOCKTAG_TUPLE) ||
409 : : (lockType == PREDLOCKTAG_PAGE))
96 michael@paquier.xyz 410 :GNC 4 : values[3] = Int32GetDatum(GET_PREDICATELOCKTARGETTAG_PAGE(*predTag));
411 : : else
5726 heikki.linnakangas@i 412 :UBC 0 : nulls[3] = true;
413 : :
414 : : /* these fields are targets for other types of locks */
5726 heikki.linnakangas@i 415 :CBC 4 : nulls[5] = true; /* virtualxid */
416 : 4 : nulls[6] = true; /* transactionid */
417 : 4 : nulls[7] = true; /* classid */
418 : 4 : nulls[8] = true; /* objid */
419 : 4 : nulls[9] = true; /* objsubid */
420 : :
421 : : /* lock holder */
953 422 : 4 : values[10] = VXIDGetDatum(xact->vxid.procNumber,
423 : : xact->vxid.localTransactionId);
5670 rhaas@postgresql.org 424 [ + - ]: 4 : if (xact->pid != 0)
425 : 4 : values[11] = Int32GetDatum(xact->pid);
426 : : else
5670 rhaas@postgresql.org 427 :UBC 0 : nulls[11] = true;
428 : :
429 : : /*
430 : : * Lock mode. Currently all predicate locks are SIReadLocks, which are
431 : : * always held (never waiting) and have no fast path
432 : : */
5726 heikki.linnakangas@i 433 :CBC 4 : values[12] = CStringGetTextDatum("SIReadLock");
434 : 4 : values[13] = BoolGetDatum(true);
5616 rhaas@postgresql.org 435 : 4 : values[14] = BoolGetDatum(false);
2065 fujii@postgresql.org 436 : 4 : nulls[15] = true;
437 : :
5726 heikki.linnakangas@i 438 : 4 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
439 : 4 : result = HeapTupleGetDatum(tuple);
440 : 4 : SRF_RETURN_NEXT(funcctx, result);
441 : : }
442 : :
8810 tgl@sss.pgh.pa.us 443 : 311 : SRF_RETURN_DONE(funcctx);
444 : : }
445 : :
446 : :
447 : : /*
448 : : * pg_blocking_pids - produce an array of the PIDs blocking given PID
449 : : *
450 : : * The reported PIDs are those that hold a lock conflicting with blocked_pid's
451 : : * current request (hard block), or are requesting such a lock and are ahead
452 : : * of blocked_pid in the lock's wait queue (soft block).
453 : : *
454 : : * In parallel-query cases, we report all PIDs blocking any member of the
455 : : * given PID's lock group, and the reported PIDs are those of the blocking
456 : : * PIDs' lock group leaders. This allows callers to compare the result to
457 : : * lists of clients' pg_backend_pid() results even during a parallel query.
458 : : *
459 : : * Parallel query makes it possible for there to be duplicate PIDs in the
460 : : * result (either because multiple waiters are blocked by same PID, or
461 : : * because multiple blockers have same group leader PID). We do not bother
462 : : * to eliminate such duplicates from the result.
463 : : *
464 : : * We need not consider predicate locks here, since those don't block anything.
465 : : */
466 : : Datum
3885 467 : 3733 : pg_blocking_pids(PG_FUNCTION_ARGS)
468 : : {
469 : 3733 : int blocked_pid = PG_GETARG_INT32(0);
470 : : Datum *arrayelems;
471 : : int narrayelems;
472 : : BlockedProcsData *lockData; /* state data from lmgr */
473 : : int i,
474 : : j;
475 : :
476 : : /* Collect a snapshot of lock manager state */
477 : 3733 : lockData = GetBlockerStatusData(blocked_pid);
478 : :
479 : : /* We can't need more output entries than there are reported PROCLOCKs */
56 michael@paquier.xyz 480 :GNC 3733 : arrayelems = palloc_array(Datum, lockData->nlocks);
3885 tgl@sss.pgh.pa.us 481 :CBC 3733 : narrayelems = 0;
482 : :
483 : : /* For each blocked proc in the lock group ... */
484 [ + + ]: 4987 : for (i = 0; i < lockData->nprocs; i++)
485 : : {
486 : 1254 : BlockedProcData *bproc = &lockData->procs[i];
487 : 1254 : LockInstanceData *instances = &lockData->locks[bproc->first_lock];
488 : 1254 : int *preceding_waiters = &lockData->waiter_pids[bproc->first_waiter];
489 : : LockInstanceData *blocked_instance;
490 : : LockMethod lockMethodTable;
491 : : int conflictMask;
492 : :
493 : : /*
494 : : * Locate the blocked proc's own entry in the LockInstanceData array.
495 : : * There should be exactly one matching entry.
496 : : */
497 : 1254 : blocked_instance = NULL;
498 [ + + ]: 3814 : for (j = 0; j < bproc->num_locks; j++)
499 : : {
500 : 2560 : LockInstanceData *instance = &(instances[j]);
501 : :
502 [ + + ]: 2560 : if (instance->pid == bproc->pid)
503 : : {
504 [ - + ]: 1254 : Assert(blocked_instance == NULL);
505 : 1254 : blocked_instance = instance;
506 : : }
507 : : }
508 [ - + ]: 1254 : Assert(blocked_instance != NULL);
509 : :
510 : 1254 : lockMethodTable = GetLockTagsMethodTable(&(blocked_instance->locktag));
511 : 1254 : conflictMask = lockMethodTable->conflictTab[blocked_instance->waitLockMode];
512 : :
513 : : /* Now scan the PROCLOCK data for conflicting procs */
514 [ + + ]: 3814 : for (j = 0; j < bproc->num_locks; j++)
515 : : {
516 : 2560 : LockInstanceData *instance = &(instances[j]);
517 : :
518 : : /* A proc never blocks itself, so ignore that entry */
519 [ + + ]: 2560 : if (instance == blocked_instance)
520 : 1254 : continue;
521 : :
522 : : /*
523 : : * Members of the same lock group don't block each other, either,
524 : : * except on relation extension locks (see LockCheckConflicts()).
525 : : */
7 nathan@postgresql.or 526 [ + + ]: 1306 : if (instance->leaderPid == blocked_instance->leaderPid &&
7 nathan@postgresql.or 527 [ + - ]:GBC 2 : blocked_instance->locktag.locktag_type !=
528 : : LOCKTAG_RELATION_EXTEND)
3885 tgl@sss.pgh.pa.us 529 : 2 : continue;
530 : :
3885 tgl@sss.pgh.pa.us 531 [ + + ]:CBC 1304 : if (conflictMask & instance->holdMask)
532 : : {
533 : : /* hard block: blocked by lock already held by this entry */
534 : : }
535 [ + + ]: 48 : else if (instance->waitLockMode != NoLock &&
536 [ + + ]: 42 : (conflictMask & LOCKBIT_ON(instance->waitLockMode)))
537 : 10 : {
538 : : /* conflict in lock requests; who's in front in wait queue? */
539 : 22 : bool ahead = false;
540 : : int k;
541 : :
542 [ + + ]: 22 : for (k = 0; k < bproc->num_waiters; k++)
543 : : {
544 [ + - ]: 10 : if (preceding_waiters[k] == instance->pid)
545 : : {
546 : : /* soft block: this entry is ahead of blocked proc */
547 : 10 : ahead = true;
548 : 10 : break;
549 : : }
550 : : }
551 [ + + ]: 22 : if (!ahead)
552 : 12 : continue; /* not blocked by this entry */
553 : : }
554 : : else
555 : : {
556 : : /* not blocked by this entry */
557 : 26 : continue;
558 : : }
559 : :
560 : : /* blocked by this entry, so emit a record */
561 : 1266 : arrayelems[narrayelems++] = Int32GetDatum(instance->leaderPid);
562 : : }
563 : : }
564 : :
565 : : /* Assert we didn't overrun arrayelems[] */
566 [ - + ]: 3733 : Assert(narrayelems <= lockData->nlocks);
567 : :
1564 peter@eisentraut.org 568 : 3733 : PG_RETURN_ARRAYTYPE_P(construct_array_builtin(arrayelems, narrayelems, INT4OID));
569 : : }
570 : :
571 : :
572 : : /*
573 : : * pg_safe_snapshot_blocking_pids - produce an array of the PIDs blocking
574 : : * given PID from getting a safe snapshot
575 : : *
576 : : * XXX this does not consider parallel-query cases; not clear how big a
577 : : * problem that is in practice
578 : : */
579 : : Datum
3472 tgl@sss.pgh.pa.us 580 :UBC 0 : pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
581 : : {
582 : 0 : int blocked_pid = PG_GETARG_INT32(0);
583 : : int *blockers;
584 : : int num_blockers;
585 : : Datum *blocker_datums;
586 : :
587 : : /* A buffer big enough for any possible blocker list without truncation */
56 michael@paquier.xyz 588 :UNC 0 : blockers = palloc_array(int, MaxBackends);
589 : :
590 : : /* Collect a snapshot of processes waited for by GetSafeSnapshot */
591 : : num_blockers =
1644 rhaas@postgresql.org 592 :UBC 0 : GetSafeSnapshotBlockingPids(blocked_pid, blockers, MaxBackends);
593 : :
594 : : /* Convert int array to Datum array */
3472 tgl@sss.pgh.pa.us 595 [ # # ]: 0 : if (num_blockers > 0)
596 : : {
597 : : int i;
598 : :
56 michael@paquier.xyz 599 :UNC 0 : blocker_datums = palloc_array(Datum, num_blockers);
3472 tgl@sss.pgh.pa.us 600 [ # # ]:UBC 0 : for (i = 0; i < num_blockers; ++i)
601 : 0 : blocker_datums[i] = Int32GetDatum(blockers[i]);
602 : : }
603 : : else
604 : 0 : blocker_datums = NULL;
605 : :
1564 peter@eisentraut.org 606 : 0 : PG_RETURN_ARRAYTYPE_P(construct_array_builtin(blocker_datums, num_blockers, INT4OID));
607 : : }
608 : :
609 : :
610 : : /*
611 : : * Functions for manipulating advisory locks
612 : : *
613 : : * We make use of the locktag fields as follows:
614 : : *
615 : : * field1: MyDatabaseId ... ensures locks are local to each database
616 : : * field2: first of 2 int4 keys, or high-order half of an int8 key
617 : : * field3: second of 2 int4 keys, or low-order half of an int8 key
618 : : * field4: 1 if using an int8 key, 2 if using 2 int4 keys
619 : : */
620 : : #define SET_LOCKTAG_INT64(tag, key64) \
621 : : SET_LOCKTAG_ADVISORY(tag, \
622 : : MyDatabaseId, \
623 : : (uint32) ((key64) >> 32), \
624 : : (uint32) (key64), \
625 : : 1)
626 : : #define SET_LOCKTAG_INT32(tag, key1, key2) \
627 : : SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, key1, key2, 2)
628 : :
629 : : /*
630 : : * pg_advisory_lock(int8) - acquire exclusive lock on an int8 key
631 : : */
632 : : Datum
7329 tgl@sss.pgh.pa.us 633 :CBC 221 : pg_advisory_lock_int8(PG_FUNCTION_ARGS)
634 : : {
635 : 221 : int64 key = PG_GETARG_INT64(0);
636 : : LOCKTAG tag;
637 : :
638 : 221 : SET_LOCKTAG_INT64(tag, key);
639 : :
640 : 221 : (void) LockAcquire(&tag, ExclusiveLock, true, false);
641 : :
642 : 220 : PG_RETURN_VOID();
643 : : }
644 : :
645 : : /*
646 : : * pg_advisory_xact_lock(int8) - acquire xact scoped
647 : : * exclusive lock on an int8 key
648 : : */
649 : : Datum
5715 itagaki.takahiro@gma 650 : 258 : pg_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
651 : : {
652 : 258 : int64 key = PG_GETARG_INT64(0);
653 : : LOCKTAG tag;
654 : :
655 : 258 : SET_LOCKTAG_INT64(tag, key);
656 : :
657 : 258 : (void) LockAcquire(&tag, ExclusiveLock, false, false);
658 : :
659 : 258 : PG_RETURN_VOID();
660 : : }
661 : :
662 : : /*
663 : : * pg_advisory_lock_shared(int8) - acquire share lock on an int8 key
664 : : */
665 : : Datum
7329 tgl@sss.pgh.pa.us 666 : 34 : pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
667 : : {
668 : 34 : int64 key = PG_GETARG_INT64(0);
669 : : LOCKTAG tag;
670 : :
671 : 34 : SET_LOCKTAG_INT64(tag, key);
672 : :
673 : 34 : (void) LockAcquire(&tag, ShareLock, true, false);
674 : :
675 : 34 : PG_RETURN_VOID();
676 : : }
677 : :
678 : : /*
679 : : * pg_advisory_xact_lock_shared(int8) - acquire xact scoped
680 : : * share lock on an int8 key
681 : : */
682 : : Datum
5715 itagaki.takahiro@gma 683 : 20025 : pg_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
684 : : {
685 : 20025 : int64 key = PG_GETARG_INT64(0);
686 : : LOCKTAG tag;
687 : :
688 : 20025 : SET_LOCKTAG_INT64(tag, key);
689 : :
690 : 20025 : (void) LockAcquire(&tag, ShareLock, false, false);
691 : :
692 : 20025 : PG_RETURN_VOID();
693 : : }
694 : :
695 : : /*
696 : : * pg_try_advisory_lock(int8) - acquire exclusive lock on an int8 key, no wait
697 : : *
698 : : * Returns true if successful, false if lock not available
699 : : */
700 : : Datum
7329 tgl@sss.pgh.pa.us 701 : 476 : pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
702 : : {
703 : 476 : int64 key = PG_GETARG_INT64(0);
704 : : LOCKTAG tag;
705 : : LockAcquireResult res;
706 : :
707 : 476 : SET_LOCKTAG_INT64(tag, key);
708 : :
709 : 476 : res = LockAcquire(&tag, ExclusiveLock, true, true);
710 : :
711 : 476 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
712 : : }
713 : :
714 : : /*
715 : : * pg_try_advisory_xact_lock(int8) - acquire xact scoped
716 : : * exclusive lock on an int8 key, no wait
717 : : *
718 : : * Returns true if successful, false if lock not available
719 : : */
720 : : Datum
5715 itagaki.takahiro@gma 721 :UBC 0 : pg_try_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
722 : : {
723 : 0 : int64 key = PG_GETARG_INT64(0);
724 : : LOCKTAG tag;
725 : : LockAcquireResult res;
726 : :
727 : 0 : SET_LOCKTAG_INT64(tag, key);
728 : :
729 : 0 : res = LockAcquire(&tag, ExclusiveLock, false, true);
730 : :
731 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
732 : : }
733 : :
734 : : /*
735 : : * pg_try_advisory_lock_shared(int8) - acquire share lock on an int8 key, no wait
736 : : *
737 : : * Returns true if successful, false if lock not available
738 : : */
739 : : Datum
7329 tgl@sss.pgh.pa.us 740 : 0 : pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
741 : : {
742 : 0 : int64 key = PG_GETARG_INT64(0);
743 : : LOCKTAG tag;
744 : : LockAcquireResult res;
745 : :
746 : 0 : SET_LOCKTAG_INT64(tag, key);
747 : :
748 : 0 : res = LockAcquire(&tag, ShareLock, true, true);
749 : :
750 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
751 : : }
752 : :
753 : : /*
754 : : * pg_try_advisory_xact_lock_shared(int8) - acquire xact scoped
755 : : * share lock on an int8 key, no wait
756 : : *
757 : : * Returns true if successful, false if lock not available
758 : : */
759 : : Datum
5715 itagaki.takahiro@gma 760 : 0 : pg_try_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
761 : : {
762 : 0 : int64 key = PG_GETARG_INT64(0);
763 : : LOCKTAG tag;
764 : : LockAcquireResult res;
765 : :
766 : 0 : SET_LOCKTAG_INT64(tag, key);
767 : :
768 : 0 : res = LockAcquire(&tag, ShareLock, false, true);
769 : :
770 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
771 : : }
772 : :
773 : : /*
774 : : * pg_advisory_unlock(int8) - release exclusive lock on an int8 key
775 : : *
776 : : * Returns true if successful, false if lock was not held
777 : : */
778 : : Datum
7329 tgl@sss.pgh.pa.us 779 :CBC 161 : pg_advisory_unlock_int8(PG_FUNCTION_ARGS)
780 : : {
781 : 161 : int64 key = PG_GETARG_INT64(0);
782 : : LOCKTAG tag;
783 : : bool res;
784 : :
785 : 161 : SET_LOCKTAG_INT64(tag, key);
786 : :
787 : 161 : res = LockRelease(&tag, ExclusiveLock, true);
788 : :
789 : 161 : PG_RETURN_BOOL(res);
790 : : }
791 : :
792 : : /*
793 : : * pg_advisory_unlock_shared(int8) - release share lock on an int8 key
794 : : *
795 : : * Returns true if successful, false if lock was not held
796 : : */
797 : : Datum
798 : 20 : pg_advisory_unlock_shared_int8(PG_FUNCTION_ARGS)
799 : : {
800 : 20 : int64 key = PG_GETARG_INT64(0);
801 : : LOCKTAG tag;
802 : : bool res;
803 : :
804 : 20 : SET_LOCKTAG_INT64(tag, key);
805 : :
806 : 20 : res = LockRelease(&tag, ShareLock, true);
807 : :
808 : 20 : PG_RETURN_BOOL(res);
809 : : }
810 : :
811 : : /*
812 : : * pg_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys
813 : : */
814 : : Datum
815 : 55 : pg_advisory_lock_int4(PG_FUNCTION_ARGS)
816 : : {
817 : 55 : int32 key1 = PG_GETARG_INT32(0);
818 : 55 : int32 key2 = PG_GETARG_INT32(1);
819 : : LOCKTAG tag;
820 : :
821 : 55 : SET_LOCKTAG_INT32(tag, key1, key2);
822 : :
823 : 55 : (void) LockAcquire(&tag, ExclusiveLock, true, false);
824 : :
825 : 55 : PG_RETURN_VOID();
826 : : }
827 : :
828 : : /*
829 : : * pg_advisory_xact_lock(int4, int4) - acquire xact scoped
830 : : * exclusive lock on 2 int4 keys
831 : : */
832 : : Datum
5715 itagaki.takahiro@gma 833 : 55 : pg_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
834 : : {
835 : 55 : int32 key1 = PG_GETARG_INT32(0);
836 : 55 : int32 key2 = PG_GETARG_INT32(1);
837 : : LOCKTAG tag;
838 : :
839 : 55 : SET_LOCKTAG_INT32(tag, key1, key2);
840 : :
841 : 55 : (void) LockAcquire(&tag, ExclusiveLock, false, false);
842 : :
843 : 55 : PG_RETURN_VOID();
844 : : }
845 : :
846 : : /*
847 : : * pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys
848 : : */
849 : : Datum
7329 tgl@sss.pgh.pa.us 850 : 24 : pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
851 : : {
852 : 24 : int32 key1 = PG_GETARG_INT32(0);
853 : 24 : int32 key2 = PG_GETARG_INT32(1);
854 : : LOCKTAG tag;
855 : :
856 : 24 : SET_LOCKTAG_INT32(tag, key1, key2);
857 : :
858 : 24 : (void) LockAcquire(&tag, ShareLock, true, false);
859 : :
860 : 24 : PG_RETURN_VOID();
861 : : }
862 : :
863 : : /*
864 : : * pg_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
865 : : * share lock on 2 int4 keys
866 : : */
867 : : Datum
5715 itagaki.takahiro@gma 868 : 20 : pg_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
869 : : {
870 : 20 : int32 key1 = PG_GETARG_INT32(0);
871 : 20 : int32 key2 = PG_GETARG_INT32(1);
872 : : LOCKTAG tag;
873 : :
874 : 20 : SET_LOCKTAG_INT32(tag, key1, key2);
875 : :
876 : 20 : (void) LockAcquire(&tag, ShareLock, false, false);
877 : :
878 : 20 : PG_RETURN_VOID();
879 : : }
880 : :
881 : : /*
882 : : * pg_try_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys, no wait
883 : : *
884 : : * Returns true if successful, false if lock not available
885 : : */
886 : : Datum
7329 tgl@sss.pgh.pa.us 887 :UBC 0 : pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
888 : : {
889 : 0 : int32 key1 = PG_GETARG_INT32(0);
890 : 0 : int32 key2 = PG_GETARG_INT32(1);
891 : : LOCKTAG tag;
892 : : LockAcquireResult res;
893 : :
894 : 0 : SET_LOCKTAG_INT32(tag, key1, key2);
895 : :
896 : 0 : res = LockAcquire(&tag, ExclusiveLock, true, true);
897 : :
898 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
899 : : }
900 : :
901 : : /*
902 : : * pg_try_advisory_xact_lock(int4, int4) - acquire xact scoped
903 : : * exclusive lock on 2 int4 keys, no wait
904 : : *
905 : : * Returns true if successful, false if lock not available
906 : : */
907 : : Datum
5715 itagaki.takahiro@gma 908 :CBC 33 : pg_try_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
909 : : {
910 : 33 : int32 key1 = PG_GETARG_INT32(0);
911 : 33 : int32 key2 = PG_GETARG_INT32(1);
912 : : LOCKTAG tag;
913 : : LockAcquireResult res;
914 : :
915 : 33 : SET_LOCKTAG_INT32(tag, key1, key2);
916 : :
917 : 33 : res = LockAcquire(&tag, ExclusiveLock, false, true);
918 : :
919 : 33 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
920 : : }
921 : :
922 : : /*
923 : : * pg_try_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys, no wait
924 : : *
925 : : * Returns true if successful, false if lock not available
926 : : */
927 : : Datum
7329 tgl@sss.pgh.pa.us 928 :UBC 0 : pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
929 : : {
930 : 0 : int32 key1 = PG_GETARG_INT32(0);
931 : 0 : int32 key2 = PG_GETARG_INT32(1);
932 : : LOCKTAG tag;
933 : : LockAcquireResult res;
934 : :
935 : 0 : SET_LOCKTAG_INT32(tag, key1, key2);
936 : :
937 : 0 : res = LockAcquire(&tag, ShareLock, true, true);
938 : :
939 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
940 : : }
941 : :
942 : : /*
943 : : * pg_try_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
944 : : * share lock on 2 int4 keys, no wait
945 : : *
946 : : * Returns true if successful, false if lock not available
947 : : */
948 : : Datum
5715 itagaki.takahiro@gma 949 : 0 : pg_try_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
950 : : {
951 : 0 : int32 key1 = PG_GETARG_INT32(0);
952 : 0 : int32 key2 = PG_GETARG_INT32(1);
953 : : LOCKTAG tag;
954 : : LockAcquireResult res;
955 : :
956 : 0 : SET_LOCKTAG_INT32(tag, key1, key2);
957 : :
958 : 0 : res = LockAcquire(&tag, ShareLock, false, true);
959 : :
960 : 0 : PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
961 : : }
962 : :
963 : : /*
964 : : * pg_advisory_unlock(int4, int4) - release exclusive lock on 2 int4 keys
965 : : *
966 : : * Returns true if successful, false if lock was not held
967 : : */
968 : : Datum
7329 tgl@sss.pgh.pa.us 969 :CBC 52 : pg_advisory_unlock_int4(PG_FUNCTION_ARGS)
970 : : {
971 : 52 : int32 key1 = PG_GETARG_INT32(0);
972 : 52 : int32 key2 = PG_GETARG_INT32(1);
973 : : LOCKTAG tag;
974 : : bool res;
975 : :
976 : 52 : SET_LOCKTAG_INT32(tag, key1, key2);
977 : :
978 : 52 : res = LockRelease(&tag, ExclusiveLock, true);
979 : :
980 : 52 : PG_RETURN_BOOL(res);
981 : : }
982 : :
983 : : /*
984 : : * pg_advisory_unlock_shared(int4, int4) - release share lock on 2 int4 keys
985 : : *
986 : : * Returns true if successful, false if lock was not held
987 : : */
988 : : Datum
989 : 20 : pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
990 : : {
991 : 20 : int32 key1 = PG_GETARG_INT32(0);
992 : 20 : int32 key2 = PG_GETARG_INT32(1);
993 : : LOCKTAG tag;
994 : : bool res;
995 : :
996 : 20 : SET_LOCKTAG_INT32(tag, key1, key2);
997 : :
998 : 20 : res = LockRelease(&tag, ShareLock, true);
999 : :
1000 : 20 : PG_RETURN_BOOL(res);
1001 : : }
1002 : :
1003 : : /*
1004 : : * pg_advisory_unlock_all() - release all advisory locks
1005 : : */
1006 : : Datum
1007 : 122 : pg_advisory_unlock_all(PG_FUNCTION_ARGS)
1008 : : {
5715 itagaki.takahiro@gma 1009 : 122 : LockReleaseSession(USER_LOCKMETHOD);
1010 : :
7329 tgl@sss.pgh.pa.us 1011 : 122 : PG_RETURN_VOID();
1012 : : }
|