]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/lockfuncs.c
55a6adaa5fa91261bc3c53caa9b374562477075b
[postgresql] / src / backend / utils / adt / lockfuncs.c
1 /*-------------------------------------------------------------------------
2  *
3  * lockfuncs.c
4  *              Functions for SQL access to various lock-manager capabilities.
5  *
6  * Copyright (c) 2002-2008, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *              $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.33 2008/03/25 22:42:44 tgl Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14
15 #include "access/heapam.h"
16 #include "catalog/pg_type.h"
17 #include "funcapi.h"
18 #include "miscadmin.h"
19 #include "storage/proc.h"
20 #include "utils/builtins.h"
21
22
23 /* This must match enum LockTagType! */
24 static const char *const LockTagTypeNames[] = {
25         "relation",
26         "extend",
27         "page",
28         "tuple",
29         "transactionid",
30         "virtualxid",
31         "object",
32         "userlock",
33         "advisory"
34 };
35
36 /* Working status for pg_lock_status */
37 typedef struct
38 {
39         LockData   *lockData;           /* state data from lmgr */
40         int                     currIdx;                /* current PROCLOCK index */
41 } PG_Lock_Status;
42
43
44 /*
45  * VXIDGetDatum - Construct a text representation of a VXID
46  *
47  * This is currently only used in pg_lock_status, so we put it here.
48  */
49 static Datum
50 VXIDGetDatum(BackendId bid, LocalTransactionId lxid)
51 {
52         /*
53          * The representation is "<bid>/<lxid>", decimal and unsigned decimal
54          * respectively.  Note that elog.c also knows how to format a vxid.
55          */
56         char            vxidstr[32];
57
58         snprintf(vxidstr, sizeof(vxidstr), "%d/%u", bid, lxid);
59
60         return CStringGetTextDatum(vxidstr);
61 }
62
63
64 /*
65  * pg_lock_status - produce a view with one row per held or awaited lock mode
66  */
67 Datum
68 pg_lock_status(PG_FUNCTION_ARGS)
69 {
70         FuncCallContext *funcctx;
71         PG_Lock_Status *mystatus;
72         LockData   *lockData;
73
74         if (SRF_IS_FIRSTCALL())
75         {
76                 TupleDesc       tupdesc;
77                 MemoryContext oldcontext;
78
79                 /* create a function context for cross-call persistence */
80                 funcctx = SRF_FIRSTCALL_INIT();
81
82                 /*
83                  * switch to memory context appropriate for multiple function calls
84                  */
85                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
86
87                 /* build tupdesc for result tuples */
88                 /* this had better match pg_locks view in system_views.sql */
89                 tupdesc = CreateTemplateTupleDesc(14, false);
90                 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype",
91                                                    TEXTOID, -1, 0);
92                 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
93                                                    OIDOID, -1, 0);
94                 TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relation",
95                                                    OIDOID, -1, 0);
96                 TupleDescInitEntry(tupdesc, (AttrNumber) 4, "page",
97                                                    INT4OID, -1, 0);
98                 TupleDescInitEntry(tupdesc, (AttrNumber) 5, "tuple",
99                                                    INT2OID, -1, 0);
100                 TupleDescInitEntry(tupdesc, (AttrNumber) 6, "virtualxid",
101                                                    TEXTOID, -1, 0);
102                 TupleDescInitEntry(tupdesc, (AttrNumber) 7, "transactionid",
103                                                    XIDOID, -1, 0);
104                 TupleDescInitEntry(tupdesc, (AttrNumber) 8, "classid",
105                                                    OIDOID, -1, 0);
106                 TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objid",
107                                                    OIDOID, -1, 0);
108                 TupleDescInitEntry(tupdesc, (AttrNumber) 10, "objsubid",
109                                                    INT2OID, -1, 0);
110                 TupleDescInitEntry(tupdesc, (AttrNumber) 11, "virtualtransaction",
111                                                    TEXTOID, -1, 0);
112                 TupleDescInitEntry(tupdesc, (AttrNumber) 12, "pid",
113                                                    INT4OID, -1, 0);
114                 TupleDescInitEntry(tupdesc, (AttrNumber) 13, "mode",
115                                                    TEXTOID, -1, 0);
116                 TupleDescInitEntry(tupdesc, (AttrNumber) 14, "granted",
117                                                    BOOLOID, -1, 0);
118
119                 funcctx->tuple_desc = BlessTupleDesc(tupdesc);
120
121                 /*
122                  * Collect all the locking information that we will format and send
123                  * out as a result set.
124                  */
125                 mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status));
126                 funcctx->user_fctx = (void *) mystatus;
127
128                 mystatus->lockData = GetLockStatusData();
129                 mystatus->currIdx = 0;
130
131                 MemoryContextSwitchTo(oldcontext);
132         }
133
134         funcctx = SRF_PERCALL_SETUP();
135         mystatus = (PG_Lock_Status *) funcctx->user_fctx;
136         lockData = mystatus->lockData;
137
138         while (mystatus->currIdx < lockData->nelements)
139         {
140                 PROCLOCK   *proclock;
141                 LOCK       *lock;
142                 PGPROC     *proc;
143                 bool            granted;
144                 LOCKMODE        mode = 0;
145                 const char *locktypename;
146                 char            tnbuf[32];
147                 Datum           values[14];
148                 char            nulls[14];
149                 HeapTuple       tuple;
150                 Datum           result;
151
152                 proclock = &(lockData->proclocks[mystatus->currIdx]);
153                 lock = &(lockData->locks[mystatus->currIdx]);
154                 proc = &(lockData->procs[mystatus->currIdx]);
155
156                 /*
157                  * Look to see if there are any held lock modes in this PROCLOCK. If
158                  * so, report, and destructively modify lockData so we don't report
159                  * again.
160                  */
161                 granted = false;
162                 if (proclock->holdMask)
163                 {
164                         for (mode = 0; mode < MAX_LOCKMODES; mode++)
165                         {
166                                 if (proclock->holdMask & LOCKBIT_ON(mode))
167                                 {
168                                         granted = true;
169                                         proclock->holdMask &= LOCKBIT_OFF(mode);
170                                         break;
171                                 }
172                         }
173                 }
174
175                 /*
176                  * If no (more) held modes to report, see if PROC is waiting for a
177                  * lock on this lock.
178                  */
179                 if (!granted)
180                 {
181                         if (proc->waitLock == proclock->tag.myLock)
182                         {
183                                 /* Yes, so report it with proper mode */
184                                 mode = proc->waitLockMode;
185
186                                 /*
187                                  * We are now done with this PROCLOCK, so advance pointer to
188                                  * continue with next one on next call.
189                                  */
190                                 mystatus->currIdx++;
191                         }
192                         else
193                         {
194                                 /*
195                                  * Okay, we've displayed all the locks associated with this
196                                  * PROCLOCK, proceed to the next one.
197                                  */
198                                 mystatus->currIdx++;
199                                 continue;
200                         }
201                 }
202
203                 /*
204                  * Form tuple with appropriate data.
205                  */
206                 MemSet(values, 0, sizeof(values));
207                 MemSet(nulls, ' ', sizeof(nulls));
208
209                 if (lock->tag.locktag_type <= LOCKTAG_LAST_TYPE)
210                         locktypename = LockTagTypeNames[lock->tag.locktag_type];
211                 else
212                 {
213                         snprintf(tnbuf, sizeof(tnbuf), "unknown %d",
214                                          (int) lock->tag.locktag_type);
215                         locktypename = tnbuf;
216                 }
217                 values[0] = CStringGetTextDatum(locktypename);
218
219                 switch ((LockTagType) lock->tag.locktag_type)
220                 {
221                         case LOCKTAG_RELATION:
222                         case LOCKTAG_RELATION_EXTEND:
223                                 values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
224                                 values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
225                                 nulls[3] = 'n';
226                                 nulls[4] = 'n';
227                                 nulls[5] = 'n';
228                                 nulls[6] = 'n';
229                                 nulls[7] = 'n';
230                                 nulls[8] = 'n';
231                                 nulls[9] = 'n';
232                                 break;
233                         case LOCKTAG_PAGE:
234                                 values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
235                                 values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
236                                 values[3] = UInt32GetDatum(lock->tag.locktag_field3);
237                                 nulls[4] = 'n';
238                                 nulls[5] = 'n';
239                                 nulls[6] = 'n';
240                                 nulls[7] = 'n';
241                                 nulls[8] = 'n';
242                                 nulls[9] = 'n';
243                                 break;
244                         case LOCKTAG_TUPLE:
245                                 values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
246                                 values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
247                                 values[3] = UInt32GetDatum(lock->tag.locktag_field3);
248                                 values[4] = UInt16GetDatum(lock->tag.locktag_field4);
249                                 nulls[5] = 'n';
250                                 nulls[6] = 'n';
251                                 nulls[7] = 'n';
252                                 nulls[8] = 'n';
253                                 nulls[9] = 'n';
254                                 break;
255                         case LOCKTAG_TRANSACTION:
256                                 values[6] = TransactionIdGetDatum(lock->tag.locktag_field1);
257                                 nulls[1] = 'n';
258                                 nulls[2] = 'n';
259                                 nulls[3] = 'n';
260                                 nulls[4] = 'n';
261                                 nulls[5] = 'n';
262                                 nulls[7] = 'n';
263                                 nulls[8] = 'n';
264                                 nulls[9] = 'n';
265                                 break;
266                         case LOCKTAG_VIRTUALTRANSACTION:
267                                 values[5] = VXIDGetDatum(lock->tag.locktag_field1,
268                                                                                  lock->tag.locktag_field2);
269                                 nulls[1] = 'n';
270                                 nulls[2] = 'n';
271                                 nulls[3] = 'n';
272                                 nulls[4] = 'n';
273                                 nulls[6] = 'n';
274                                 nulls[7] = 'n';
275                                 nulls[8] = 'n';
276                                 nulls[9] = 'n';
277                                 break;
278                         case LOCKTAG_OBJECT:
279                         case LOCKTAG_USERLOCK:
280                         case LOCKTAG_ADVISORY:
281                         default:                        /* treat unknown locktags like OBJECT */
282                                 values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
283                                 values[7] = ObjectIdGetDatum(lock->tag.locktag_field2);
284                                 values[8] = ObjectIdGetDatum(lock->tag.locktag_field3);
285                                 values[9] = Int16GetDatum(lock->tag.locktag_field4);
286                                 nulls[2] = 'n';
287                                 nulls[3] = 'n';
288                                 nulls[4] = 'n';
289                                 nulls[5] = 'n';
290                                 nulls[6] = 'n';
291                                 break;
292                 }
293
294                 values[10] = VXIDGetDatum(proc->backendId, proc->lxid);
295                 if (proc->pid != 0)
296                         values[11] = Int32GetDatum(proc->pid);
297                 else
298                         nulls[11] = 'n';
299                 values[12] = CStringGetTextDatum(GetLockmodeName(LOCK_LOCKMETHOD(*lock), mode));
300                 values[13] = BoolGetDatum(granted);
301
302                 tuple = heap_formtuple(funcctx->tuple_desc, values, nulls);
303                 result = HeapTupleGetDatum(tuple);
304                 SRF_RETURN_NEXT(funcctx, result);
305         }
306
307         SRF_RETURN_DONE(funcctx);
308 }
309
310
311 /*
312  * Functions for manipulating advisory locks
313  *
314  * We make use of the locktag fields as follows:
315  *
316  *      field1: MyDatabaseId ... ensures locks are local to each database
317  *      field2: first of 2 int4 keys, or high-order half of an int8 key
318  *      field3: second of 2 int4 keys, or low-order half of an int8 key
319  *      field4: 1 if using an int8 key, 2 if using 2 int4 keys
320  */
321 #define SET_LOCKTAG_INT64(tag, key64) \
322         SET_LOCKTAG_ADVISORY(tag, \
323                                                  MyDatabaseId, \
324                                                  (uint32) ((key64) >> 32), \
325                                                  (uint32) (key64), \
326                                                  1)
327 #define SET_LOCKTAG_INT32(tag, key1, key2) \
328         SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, key1, key2, 2)
329
330 /*
331  * pg_advisory_lock(int8) - acquire exclusive lock on an int8 key
332  */
333 Datum
334 pg_advisory_lock_int8(PG_FUNCTION_ARGS)
335 {
336         int64           key = PG_GETARG_INT64(0);
337         LOCKTAG         tag;
338
339         SET_LOCKTAG_INT64(tag, key);
340
341         (void) LockAcquire(&tag, ExclusiveLock, true, false);
342
343         PG_RETURN_VOID();
344 }
345
346 /*
347  * pg_advisory_lock_shared(int8) - acquire share lock on an int8 key
348  */
349 Datum
350 pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
351 {
352         int64           key = PG_GETARG_INT64(0);
353         LOCKTAG         tag;
354
355         SET_LOCKTAG_INT64(tag, key);
356
357         (void) LockAcquire(&tag, ShareLock, true, false);
358
359         PG_RETURN_VOID();
360 }
361
362 /*
363  * pg_try_advisory_lock(int8) - acquire exclusive lock on an int8 key, no wait
364  *
365  * Returns true if successful, false if lock not available
366  */
367 Datum
368 pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
369 {
370         int64           key = PG_GETARG_INT64(0);
371         LOCKTAG         tag;
372         LockAcquireResult res;
373
374         SET_LOCKTAG_INT64(tag, key);
375
376         res = LockAcquire(&tag, ExclusiveLock, true, true);
377
378         PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
379 }
380
381 /*
382  * pg_try_advisory_lock_shared(int8) - acquire share lock on an int8 key, no wait
383  *
384  * Returns true if successful, false if lock not available
385  */
386 Datum
387 pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
388 {
389         int64           key = PG_GETARG_INT64(0);
390         LOCKTAG         tag;
391         LockAcquireResult res;
392
393         SET_LOCKTAG_INT64(tag, key);
394
395         res = LockAcquire(&tag, ShareLock, true, true);
396
397         PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
398 }
399
400 /*
401  * pg_advisory_unlock(int8) - release exclusive lock on an int8 key
402  *
403  * Returns true if successful, false if lock was not held
404 */
405 Datum
406 pg_advisory_unlock_int8(PG_FUNCTION_ARGS)
407 {
408         int64           key = PG_GETARG_INT64(0);
409         LOCKTAG         tag;
410         bool            res;
411
412         SET_LOCKTAG_INT64(tag, key);
413
414         res = LockRelease(&tag, ExclusiveLock, true);
415
416         PG_RETURN_BOOL(res);
417 }
418
419 /*
420  * pg_advisory_unlock_shared(int8) - release share lock on an int8 key
421  *
422  * Returns true if successful, false if lock was not held
423  */
424 Datum
425 pg_advisory_unlock_shared_int8(PG_FUNCTION_ARGS)
426 {
427         int64           key = PG_GETARG_INT64(0);
428         LOCKTAG         tag;
429         bool            res;
430
431         SET_LOCKTAG_INT64(tag, key);
432
433         res = LockRelease(&tag, ShareLock, true);
434
435         PG_RETURN_BOOL(res);
436 }
437
438 /*
439  * pg_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys
440  */
441 Datum
442 pg_advisory_lock_int4(PG_FUNCTION_ARGS)
443 {
444         int32           key1 = PG_GETARG_INT32(0);
445         int32           key2 = PG_GETARG_INT32(1);
446         LOCKTAG         tag;
447
448         SET_LOCKTAG_INT32(tag, key1, key2);
449
450         (void) LockAcquire(&tag, ExclusiveLock, true, false);
451
452         PG_RETURN_VOID();
453 }
454
455 /*
456  * pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys
457  */
458 Datum
459 pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
460 {
461         int32           key1 = PG_GETARG_INT32(0);
462         int32           key2 = PG_GETARG_INT32(1);
463         LOCKTAG         tag;
464
465         SET_LOCKTAG_INT32(tag, key1, key2);
466
467         (void) LockAcquire(&tag, ShareLock, true, false);
468
469         PG_RETURN_VOID();
470 }
471
472 /*
473  * pg_try_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys, no wait
474  *
475  * Returns true if successful, false if lock not available
476  */
477 Datum
478 pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
479 {
480         int32           key1 = PG_GETARG_INT32(0);
481         int32           key2 = PG_GETARG_INT32(1);
482         LOCKTAG         tag;
483         LockAcquireResult res;
484
485         SET_LOCKTAG_INT32(tag, key1, key2);
486
487         res = LockAcquire(&tag, ExclusiveLock, true, true);
488
489         PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
490 }
491
492 /*
493  * pg_try_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys, no wait
494  *
495  * Returns true if successful, false if lock not available
496  */
497 Datum
498 pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
499 {
500         int32           key1 = PG_GETARG_INT32(0);
501         int32           key2 = PG_GETARG_INT32(1);
502         LOCKTAG         tag;
503         LockAcquireResult res;
504
505         SET_LOCKTAG_INT32(tag, key1, key2);
506
507         res = LockAcquire(&tag, ShareLock, true, true);
508
509         PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
510 }
511
512 /*
513  * pg_advisory_unlock(int4, int4) - release exclusive lock on 2 int4 keys
514  *
515  * Returns true if successful, false if lock was not held
516 */
517 Datum
518 pg_advisory_unlock_int4(PG_FUNCTION_ARGS)
519 {
520         int32           key1 = PG_GETARG_INT32(0);
521         int32           key2 = PG_GETARG_INT32(1);
522         LOCKTAG         tag;
523         bool            res;
524
525         SET_LOCKTAG_INT32(tag, key1, key2);
526
527         res = LockRelease(&tag, ExclusiveLock, true);
528
529         PG_RETURN_BOOL(res);
530 }
531
532 /*
533  * pg_advisory_unlock_shared(int4, int4) - release share lock on 2 int4 keys
534  *
535  * Returns true if successful, false if lock was not held
536  */
537 Datum
538 pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
539 {
540         int32           key1 = PG_GETARG_INT32(0);
541         int32           key2 = PG_GETARG_INT32(1);
542         LOCKTAG         tag;
543         bool            res;
544
545         SET_LOCKTAG_INT32(tag, key1, key2);
546
547         res = LockRelease(&tag, ShareLock, true);
548
549         PG_RETURN_BOOL(res);
550 }
551
552 /*
553  * pg_advisory_unlock_all() - release all advisory locks
554  */
555 Datum
556 pg_advisory_unlock_all(PG_FUNCTION_ARGS)
557 {
558         LockReleaseAll(USER_LOCKMETHOD, true);
559
560         PG_RETURN_VOID();
561 }