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