]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/lockfuncs.c
Update copyrights in source tree to 2008.
[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.31 2008/01/01 19:45:52 momjian 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 DirectFunctionCall1(textin, CStringGetDatum(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_ADVISORY)
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] = DirectFunctionCall1(textin,
218                                                                                 CStringGetDatum(locktypename));
219
220                 switch (lock->tag.locktag_type)
221                 {
222                         case LOCKTAG_RELATION:
223                         case LOCKTAG_RELATION_EXTEND:
224                                 values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
225                                 values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
226                                 nulls[3] = 'n';
227                                 nulls[4] = 'n';
228                                 nulls[5] = 'n';
229                                 nulls[6] = 'n';
230                                 nulls[7] = 'n';
231                                 nulls[8] = 'n';
232                                 nulls[9] = 'n';
233                                 break;
234                         case LOCKTAG_PAGE:
235                                 values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
236                                 values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
237                                 values[3] = UInt32GetDatum(lock->tag.locktag_field3);
238                                 nulls[4] = 'n';
239                                 nulls[5] = 'n';
240                                 nulls[6] = 'n';
241                                 nulls[7] = 'n';
242                                 nulls[8] = 'n';
243                                 nulls[9] = 'n';
244                                 break;
245                         case LOCKTAG_TUPLE:
246                                 values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
247                                 values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
248                                 values[3] = UInt32GetDatum(lock->tag.locktag_field3);
249                                 values[4] = UInt16GetDatum(lock->tag.locktag_field4);
250                                 nulls[5] = 'n';
251                                 nulls[6] = 'n';
252                                 nulls[7] = 'n';
253                                 nulls[8] = 'n';
254                                 nulls[9] = 'n';
255                                 break;
256                         case LOCKTAG_TRANSACTION:
257                                 values[6] = TransactionIdGetDatum(lock->tag.locktag_field1);
258                                 nulls[1] = 'n';
259                                 nulls[2] = 'n';
260                                 nulls[3] = 'n';
261                                 nulls[4] = 'n';
262                                 nulls[5] = 'n';
263                                 nulls[7] = 'n';
264                                 nulls[8] = 'n';
265                                 nulls[9] = 'n';
266                                 break;
267                         case LOCKTAG_VIRTUALTRANSACTION:
268                                 values[5] = VXIDGetDatum(lock->tag.locktag_field1,
269                                                                                  lock->tag.locktag_field2);
270                                 nulls[1] = 'n';
271                                 nulls[2] = 'n';
272                                 nulls[3] = 'n';
273                                 nulls[4] = 'n';
274                                 nulls[6] = 'n';
275                                 nulls[7] = 'n';
276                                 nulls[8] = 'n';
277                                 nulls[9] = 'n';
278                                 break;
279                         case LOCKTAG_OBJECT:
280                         case LOCKTAG_USERLOCK:
281                         case LOCKTAG_ADVISORY:
282                         default:                        /* treat unknown locktags like OBJECT */
283                                 values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
284                                 values[7] = ObjectIdGetDatum(lock->tag.locktag_field2);
285                                 values[8] = ObjectIdGetDatum(lock->tag.locktag_field3);
286                                 values[9] = Int16GetDatum(lock->tag.locktag_field4);
287                                 nulls[2] = 'n';
288                                 nulls[3] = 'n';
289                                 nulls[4] = 'n';
290                                 nulls[5] = 'n';
291                                 nulls[6] = 'n';
292                                 break;
293                 }
294
295                 values[10] = VXIDGetDatum(proc->backendId, proc->lxid);
296                 if (proc->pid != 0)
297                         values[11] = Int32GetDatum(proc->pid);
298                 else
299                         nulls[11] = 'n';
300                 values[12] = DirectFunctionCall1(textin,
301                                           CStringGetDatum(GetLockmodeName(LOCK_LOCKMETHOD(*lock),
302                                                                                                           mode)));
303                 values[13] = BoolGetDatum(granted);
304
305                 tuple = heap_formtuple(funcctx->tuple_desc, values, nulls);
306                 result = HeapTupleGetDatum(tuple);
307                 SRF_RETURN_NEXT(funcctx, result);
308         }
309
310         SRF_RETURN_DONE(funcctx);
311 }
312
313
314 /*
315  * Functions for manipulating advisory locks
316  *
317  * We make use of the locktag fields as follows:
318  *
319  *      field1: MyDatabaseId ... ensures locks are local to each database
320  *      field2: first of 2 int4 keys, or high-order half of an int8 key
321  *      field3: second of 2 int4 keys, or low-order half of an int8 key
322  *      field4: 1 if using an int8 key, 2 if using 2 int4 keys
323  */
324 #define SET_LOCKTAG_INT64(tag, key64) \
325         SET_LOCKTAG_ADVISORY(tag, \
326                                                  MyDatabaseId, \
327                                                  (uint32) ((key64) >> 32), \
328                                                  (uint32) (key64), \
329                                                  1)
330 #define SET_LOCKTAG_INT32(tag, key1, key2) \
331         SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, key1, key2, 2)
332
333 /*
334  * pg_advisory_lock(int8) - acquire exclusive lock on an int8 key
335  */
336 Datum
337 pg_advisory_lock_int8(PG_FUNCTION_ARGS)
338 {
339         int64           key = PG_GETARG_INT64(0);
340         LOCKTAG         tag;
341
342         SET_LOCKTAG_INT64(tag, key);
343
344         (void) LockAcquire(&tag, ExclusiveLock, true, false);
345
346         PG_RETURN_VOID();
347 }
348
349 /*
350  * pg_advisory_lock_shared(int8) - acquire share lock on an int8 key
351  */
352 Datum
353 pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
354 {
355         int64           key = PG_GETARG_INT64(0);
356         LOCKTAG         tag;
357
358         SET_LOCKTAG_INT64(tag, key);
359
360         (void) LockAcquire(&tag, ShareLock, true, false);
361
362         PG_RETURN_VOID();
363 }
364
365 /*
366  * pg_try_advisory_lock(int8) - acquire exclusive lock on an int8 key, no wait
367  *
368  * Returns true if successful, false if lock not available
369  */
370 Datum
371 pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
372 {
373         int64           key = PG_GETARG_INT64(0);
374         LOCKTAG         tag;
375         LockAcquireResult res;
376
377         SET_LOCKTAG_INT64(tag, key);
378
379         res = LockAcquire(&tag, ExclusiveLock, true, true);
380
381         PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
382 }
383
384 /*
385  * pg_try_advisory_lock_shared(int8) - acquire share lock on an int8 key, no wait
386  *
387  * Returns true if successful, false if lock not available
388  */
389 Datum
390 pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
391 {
392         int64           key = PG_GETARG_INT64(0);
393         LOCKTAG         tag;
394         LockAcquireResult res;
395
396         SET_LOCKTAG_INT64(tag, key);
397
398         res = LockAcquire(&tag, ShareLock, true, true);
399
400         PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
401 }
402
403 /*
404  * pg_advisory_unlock(int8) - release exclusive lock on an int8 key
405  *
406  * Returns true if successful, false if lock was not held
407 */
408 Datum
409 pg_advisory_unlock_int8(PG_FUNCTION_ARGS)
410 {
411         int64           key = PG_GETARG_INT64(0);
412         LOCKTAG         tag;
413         bool            res;
414
415         SET_LOCKTAG_INT64(tag, key);
416
417         res = LockRelease(&tag, ExclusiveLock, true);
418
419         PG_RETURN_BOOL(res);
420 }
421
422 /*
423  * pg_advisory_unlock_shared(int8) - release share lock on an int8 key
424  *
425  * Returns true if successful, false if lock was not held
426  */
427 Datum
428 pg_advisory_unlock_shared_int8(PG_FUNCTION_ARGS)
429 {
430         int64           key = PG_GETARG_INT64(0);
431         LOCKTAG         tag;
432         bool            res;
433
434         SET_LOCKTAG_INT64(tag, key);
435
436         res = LockRelease(&tag, ShareLock, true);
437
438         PG_RETURN_BOOL(res);
439 }
440
441 /*
442  * pg_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys
443  */
444 Datum
445 pg_advisory_lock_int4(PG_FUNCTION_ARGS)
446 {
447         int32           key1 = PG_GETARG_INT32(0);
448         int32           key2 = PG_GETARG_INT32(1);
449         LOCKTAG         tag;
450
451         SET_LOCKTAG_INT32(tag, key1, key2);
452
453         (void) LockAcquire(&tag, ExclusiveLock, true, false);
454
455         PG_RETURN_VOID();
456 }
457
458 /*
459  * pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys
460  */
461 Datum
462 pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
463 {
464         int32           key1 = PG_GETARG_INT32(0);
465         int32           key2 = PG_GETARG_INT32(1);
466         LOCKTAG         tag;
467
468         SET_LOCKTAG_INT32(tag, key1, key2);
469
470         (void) LockAcquire(&tag, ShareLock, true, false);
471
472         PG_RETURN_VOID();
473 }
474
475 /*
476  * pg_try_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys, no wait
477  *
478  * Returns true if successful, false if lock not available
479  */
480 Datum
481 pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
482 {
483         int32           key1 = PG_GETARG_INT32(0);
484         int32           key2 = PG_GETARG_INT32(1);
485         LOCKTAG         tag;
486         LockAcquireResult res;
487
488         SET_LOCKTAG_INT32(tag, key1, key2);
489
490         res = LockAcquire(&tag, ExclusiveLock, true, true);
491
492         PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
493 }
494
495 /*
496  * pg_try_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys, no wait
497  *
498  * Returns true if successful, false if lock not available
499  */
500 Datum
501 pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
502 {
503         int32           key1 = PG_GETARG_INT32(0);
504         int32           key2 = PG_GETARG_INT32(1);
505         LOCKTAG         tag;
506         LockAcquireResult res;
507
508         SET_LOCKTAG_INT32(tag, key1, key2);
509
510         res = LockAcquire(&tag, ShareLock, true, true);
511
512         PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
513 }
514
515 /*
516  * pg_advisory_unlock(int4, int4) - release exclusive lock on 2 int4 keys
517  *
518  * Returns true if successful, false if lock was not held
519 */
520 Datum
521 pg_advisory_unlock_int4(PG_FUNCTION_ARGS)
522 {
523         int32           key1 = PG_GETARG_INT32(0);
524         int32           key2 = PG_GETARG_INT32(1);
525         LOCKTAG         tag;
526         bool            res;
527
528         SET_LOCKTAG_INT32(tag, key1, key2);
529
530         res = LockRelease(&tag, ExclusiveLock, true);
531
532         PG_RETURN_BOOL(res);
533 }
534
535 /*
536  * pg_advisory_unlock_shared(int4, int4) - release share lock on 2 int4 keys
537  *
538  * Returns true if successful, false if lock was not held
539  */
540 Datum
541 pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
542 {
543         int32           key1 = PG_GETARG_INT32(0);
544         int32           key2 = PG_GETARG_INT32(1);
545         LOCKTAG         tag;
546         bool            res;
547
548         SET_LOCKTAG_INT32(tag, key1, key2);
549
550         res = LockRelease(&tag, ShareLock, true);
551
552         PG_RETURN_BOOL(res);
553 }
554
555 /*
556  * pg_advisory_unlock_all() - release all advisory locks
557  */
558 Datum
559 pg_advisory_unlock_all(PG_FUNCTION_ARGS)
560 {
561         LockReleaseAll(USER_LOCKMETHOD, true);
562
563         PG_RETURN_VOID();
564 }