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