LCOV - code coverage report
Current view: top level - src/backend/utils/adt - lockfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 289 384 75.3 %
Date: 2023-05-31 03:12:18 Functions: 19 26 73.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * lockfuncs.c
       4             :  *      Functions for SQL access to various lock-manager capabilities.
       5             :  *
       6             :  * Copyright (c) 2002-2023, 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 "access/xact.h"
      17             : #include "catalog/pg_type.h"
      18             : #include "funcapi.h"
      19             : #include "miscadmin.h"
      20             : #include "storage/predicate_internals.h"
      21             : #include "utils/array.h"
      22             : #include "utils/builtins.h"
      23             : 
      24             : 
      25             : /*
      26             :  * This must match enum LockTagType!  Also, be sure to document any changes
      27             :  * in the docs for the pg_locks view and for wait event types.
      28             :  */
      29             : const char *const LockTagTypeNames[] = {
      30             :     "relation",
      31             :     "extend",
      32             :     "frozenid",
      33             :     "page",
      34             :     "tuple",
      35             :     "transactionid",
      36             :     "virtualxid",
      37             :     "spectoken",
      38             :     "object",
      39             :     "userlock",
      40             :     "advisory",
      41             :     "applytransaction"
      42             : };
      43             : 
      44             : StaticAssertDecl(lengthof(LockTagTypeNames) == (LOCKTAG_LAST_TYPE + 1),
      45             :                  "array length mismatch");
      46             : 
      47             : /* This must match enum PredicateLockTargetType (predicate_internals.h) */
      48             : static const char *const PredicateLockTagTypeNames[] = {
      49             :     "relation",
      50             :     "page",
      51             :     "tuple"
      52             : };
      53             : 
      54             : StaticAssertDecl(lengthof(PredicateLockTagTypeNames) == (PREDLOCKTAG_TUPLE + 1),
      55             :                  "array length mismatch");
      56             : 
      57             : /* Working status for pg_lock_status */
      58             : typedef struct
      59             : {
      60             :     LockData   *lockData;       /* state data from lmgr */
      61             :     int         currIdx;        /* current PROCLOCK index */
      62             :     PredicateLockData *predLockData;    /* state data for pred locks */
      63             :     int         predLockIdx;    /* current index for pred lock */
      64             : } PG_Lock_Status;
      65             : 
      66             : /* Number of columns in pg_locks output */
      67             : #define NUM_LOCK_STATUS_COLUMNS     16
      68             : 
      69             : /*
      70             :  * VXIDGetDatum - Construct a text representation of a VXID
      71             :  *
      72             :  * This is currently only used in pg_lock_status, so we put it here.
      73             :  */
      74             : static Datum
      75       13064 : VXIDGetDatum(BackendId bid, LocalTransactionId lxid)
      76             : {
      77             :     /*
      78             :      * The representation is "<bid>/<lxid>", decimal and unsigned decimal
      79             :      * respectively.  Note that elog.c also knows how to format a vxid.
      80             :      */
      81             :     char        vxidstr[32];
      82             : 
      83       13064 :     snprintf(vxidstr, sizeof(vxidstr), "%d/%u", bid, lxid);
      84             : 
      85       13064 :     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
      93       11882 : pg_lock_status(PG_FUNCTION_ARGS)
      94             : {
      95             :     FuncCallContext *funcctx;
      96             :     PG_Lock_Status *mystatus;
      97             :     LockData   *lockData;
      98             :     PredicateLockData *predLockData;
      99             : 
     100       11882 :     if (SRF_IS_FIRSTCALL())
     101             :     {
     102             :         TupleDesc   tupdesc;
     103             :         MemoryContext oldcontext;
     104             : 
     105             :         /* create a function context for cross-call persistence */
     106         568 :         funcctx = SRF_FIRSTCALL_INIT();
     107             : 
     108             :         /*
     109             :          * switch to memory context appropriate for multiple function calls
     110             :          */
     111         568 :         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 */
     115         568 :         tupdesc = CreateTemplateTupleDesc(NUM_LOCK_STATUS_COLUMNS);
     116         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype",
     117             :                            TEXTOID, -1, 0);
     118         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
     119             :                            OIDOID, -1, 0);
     120         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relation",
     121             :                            OIDOID, -1, 0);
     122         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 4, "page",
     123             :                            INT4OID, -1, 0);
     124         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 5, "tuple",
     125             :                            INT2OID, -1, 0);
     126         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 6, "virtualxid",
     127             :                            TEXTOID, -1, 0);
     128         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 7, "transactionid",
     129             :                            XIDOID, -1, 0);
     130         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 8, "classid",
     131             :                            OIDOID, -1, 0);
     132         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objid",
     133             :                            OIDOID, -1, 0);
     134         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 10, "objsubid",
     135             :                            INT2OID, -1, 0);
     136         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 11, "virtualtransaction",
     137             :                            TEXTOID, -1, 0);
     138         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 12, "pid",
     139             :                            INT4OID, -1, 0);
     140         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 13, "mode",
     141             :                            TEXTOID, -1, 0);
     142         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 14, "granted",
     143             :                            BOOLOID, -1, 0);
     144         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 15, "fastpath",
     145             :                            BOOLOID, -1, 0);
     146         568 :         TupleDescInitEntry(tupdesc, (AttrNumber) 16, "waitstart",
     147             :                            TIMESTAMPTZOID, -1, 0);
     148             : 
     149         568 :         funcctx->tuple_desc = BlessTupleDesc(tupdesc);
     150             : 
     151             :         /*
     152             :          * Collect all the locking information that we will format and send
     153             :          * out as a result set.
     154             :          */
     155         568 :         mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status));
     156         568 :         funcctx->user_fctx = (void *) mystatus;
     157             : 
     158         568 :         mystatus->lockData = GetLockStatusData();
     159         568 :         mystatus->currIdx = 0;
     160         568 :         mystatus->predLockData = GetPredicateLockStatusData();
     161         568 :         mystatus->predLockIdx = 0;
     162             : 
     163         568 :         MemoryContextSwitchTo(oldcontext);
     164             :     }
     165             : 
     166       11882 :     funcctx = SRF_PERCALL_SETUP();
     167       11882 :     mystatus = (PG_Lock_Status *) funcctx->user_fctx;
     168       11882 :     lockData = mystatus->lockData;
     169             : 
     170       22770 :     while (mystatus->currIdx < lockData->nelements)
     171             :     {
     172             :         bool        granted;
     173       22196 :         LOCKMODE    mode = 0;
     174             :         const char *locktypename;
     175             :         char        tnbuf[32];
     176       22196 :         Datum       values[NUM_LOCK_STATUS_COLUMNS] = {0};
     177       22196 :         bool        nulls[NUM_LOCK_STATUS_COLUMNS] = {0};
     178             :         HeapTuple   tuple;
     179             :         Datum       result;
     180             :         LockInstanceData *instance;
     181             : 
     182       22196 :         instance = &(lockData->locks[mystatus->currIdx]);
     183             : 
     184             :         /*
     185             :          * Look to see if there are any held lock modes in this PROCLOCK. If
     186             :          * so, report, and destructively modify lockData so we don't report
     187             :          * again.
     188             :          */
     189       22196 :         granted = false;
     190       22196 :         if (instance->holdMask)
     191             :         {
     192       50310 :             for (mode = 0; mode < MAX_LOCKMODES; mode++)
     193             :             {
     194       50310 :                 if (instance->holdMask & LOCKBIT_ON(mode))
     195             :                 {
     196       11292 :                     granted = true;
     197       11292 :                     instance->holdMask &= LOCKBIT_OFF(mode);
     198       11292 :                     break;
     199             :                 }
     200             :             }
     201             :         }
     202             : 
     203             :         /*
     204             :          * If no (more) held modes to report, see if PROC is waiting for a
     205             :          * lock on this lock.
     206             :          */
     207       22196 :         if (!granted)
     208             :         {
     209       10904 :             if (instance->waitLockMode != NoLock)
     210             :             {
     211             :                 /* Yes, so report it with proper mode */
     212          16 :                 mode = instance->waitLockMode;
     213             : 
     214             :                 /*
     215             :                  * We are now done with this PROCLOCK, so advance pointer to
     216             :                  * continue with next one on next call.
     217             :                  */
     218          16 :                 mystatus->currIdx++;
     219             :             }
     220             :             else
     221             :             {
     222             :                 /*
     223             :                  * Okay, we've displayed all the locks associated with this
     224             :                  * PROCLOCK, proceed to the next one.
     225             :                  */
     226       10888 :                 mystatus->currIdx++;
     227       10888 :                 continue;
     228             :             }
     229             :         }
     230             : 
     231             :         /*
     232             :          * Form tuple with appropriate data.
     233             :          */
     234             : 
     235       11308 :         if (instance->locktag.locktag_type <= LOCKTAG_LAST_TYPE)
     236       11308 :             locktypename = LockTagTypeNames[instance->locktag.locktag_type];
     237             :         else
     238             :         {
     239           0 :             snprintf(tnbuf, sizeof(tnbuf), "unknown %d",
     240           0 :                      (int) instance->locktag.locktag_type);
     241           0 :             locktypename = tnbuf;
     242             :         }
     243       11308 :         values[0] = CStringGetTextDatum(locktypename);
     244             : 
     245       11308 :         switch ((LockTagType) instance->locktag.locktag_type)
     246             :         {
     247        7776 :             case LOCKTAG_RELATION:
     248             :             case LOCKTAG_RELATION_EXTEND:
     249        7776 :                 values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
     250        7776 :                 values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
     251        7776 :                 nulls[3] = true;
     252        7776 :                 nulls[4] = true;
     253        7776 :                 nulls[5] = true;
     254        7776 :                 nulls[6] = true;
     255        7776 :                 nulls[7] = true;
     256        7776 :                 nulls[8] = true;
     257        7776 :                 nulls[9] = true;
     258        7776 :                 break;
     259           0 :             case LOCKTAG_DATABASE_FROZEN_IDS:
     260           0 :                 values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
     261           0 :                 nulls[2] = true;
     262           0 :                 nulls[3] = true;
     263           0 :                 nulls[4] = true;
     264           0 :                 nulls[5] = true;
     265           0 :                 nulls[6] = true;
     266           0 :                 nulls[7] = true;
     267           0 :                 nulls[8] = true;
     268           0 :                 nulls[9] = true;
     269           0 :                 break;
     270           0 :             case LOCKTAG_PAGE:
     271           0 :                 values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
     272           0 :                 values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
     273           0 :                 values[3] = UInt32GetDatum(instance->locktag.locktag_field3);
     274           0 :                 nulls[4] = true;
     275           0 :                 nulls[5] = true;
     276           0 :                 nulls[6] = true;
     277           0 :                 nulls[7] = true;
     278           0 :                 nulls[8] = true;
     279           0 :                 nulls[9] = true;
     280           0 :                 break;
     281           0 :             case LOCKTAG_TUPLE:
     282           0 :                 values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
     283           0 :                 values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
     284           0 :                 values[3] = UInt32GetDatum(instance->locktag.locktag_field3);
     285           0 :                 values[4] = UInt16GetDatum(instance->locktag.locktag_field4);
     286           0 :                 nulls[5] = true;
     287           0 :                 nulls[6] = true;
     288           0 :                 nulls[7] = true;
     289           0 :                 nulls[8] = true;
     290           0 :                 nulls[9] = true;
     291           0 :                 break;
     292         850 :             case LOCKTAG_TRANSACTION:
     293         850 :                 values[6] =
     294         850 :                     TransactionIdGetDatum(instance->locktag.locktag_field1);
     295         850 :                 nulls[1] = true;
     296         850 :                 nulls[2] = true;
     297         850 :                 nulls[3] = true;
     298         850 :                 nulls[4] = true;
     299         850 :                 nulls[5] = true;
     300         850 :                 nulls[7] = true;
     301         850 :                 nulls[8] = true;
     302         850 :                 nulls[9] = true;
     303         850 :                 break;
     304        1750 :             case LOCKTAG_VIRTUALTRANSACTION:
     305        1750 :                 values[5] = VXIDGetDatum(instance->locktag.locktag_field1,
     306             :                                          instance->locktag.locktag_field2);
     307        1750 :                 nulls[1] = true;
     308        1750 :                 nulls[2] = true;
     309        1750 :                 nulls[3] = true;
     310        1750 :                 nulls[4] = true;
     311        1750 :                 nulls[6] = true;
     312        1750 :                 nulls[7] = true;
     313        1750 :                 nulls[8] = true;
     314        1750 :                 nulls[9] = true;
     315        1750 :                 break;
     316           4 :             case LOCKTAG_SPECULATIVE_TOKEN:
     317           4 :                 values[6] =
     318           4 :                     TransactionIdGetDatum(instance->locktag.locktag_field1);
     319           4 :                 values[8] = ObjectIdGetDatum(instance->locktag.locktag_field2);
     320           4 :                 nulls[1] = true;
     321           4 :                 nulls[2] = true;
     322           4 :                 nulls[3] = true;
     323           4 :                 nulls[4] = true;
     324           4 :                 nulls[5] = true;
     325           4 :                 nulls[7] = true;
     326           4 :                 nulls[9] = true;
     327           4 :                 break;
     328           0 :             case LOCKTAG_APPLY_TRANSACTION:
     329           0 :                 values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
     330           0 :                 values[8] = ObjectIdGetDatum(instance->locktag.locktag_field2);
     331           0 :                 values[6] = ObjectIdGetDatum(instance->locktag.locktag_field3);
     332           0 :                 values[9] = Int16GetDatum(instance->locktag.locktag_field4);
     333           0 :                 nulls[2] = true;
     334           0 :                 nulls[3] = true;
     335           0 :                 nulls[4] = true;
     336           0 :                 nulls[5] = true;
     337           0 :                 nulls[7] = true;
     338           0 :                 break;
     339         928 :             case LOCKTAG_OBJECT:
     340             :             case LOCKTAG_USERLOCK:
     341             :             case LOCKTAG_ADVISORY:
     342             :             default:            /* treat unknown locktags like OBJECT */
     343         928 :                 values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
     344         928 :                 values[7] = ObjectIdGetDatum(instance->locktag.locktag_field2);
     345         928 :                 values[8] = ObjectIdGetDatum(instance->locktag.locktag_field3);
     346         928 :                 values[9] = Int16GetDatum(instance->locktag.locktag_field4);
     347         928 :                 nulls[2] = true;
     348         928 :                 nulls[3] = true;
     349         928 :                 nulls[4] = true;
     350         928 :                 nulls[5] = true;
     351         928 :                 nulls[6] = true;
     352         928 :                 break;
     353             :         }
     354             : 
     355       11308 :         values[10] = VXIDGetDatum(instance->backend, instance->lxid);
     356       11308 :         if (instance->pid != 0)
     357       11270 :             values[11] = Int32GetDatum(instance->pid);
     358             :         else
     359          38 :             nulls[11] = true;
     360       11308 :         values[12] = CStringGetTextDatum(GetLockmodeName(instance->locktag.locktag_lockmethodid, mode));
     361       11308 :         values[13] = BoolGetDatum(granted);
     362       11308 :         values[14] = BoolGetDatum(instance->fastpath);
     363       11308 :         if (!granted && instance->waitStart != 0)
     364          16 :             values[15] = TimestampTzGetDatum(instance->waitStart);
     365             :         else
     366       11292 :             nulls[15] = true;
     367             : 
     368       11308 :         tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
     369       11308 :         result = HeapTupleGetDatum(tuple);
     370       11308 :         SRF_RETURN_NEXT(funcctx, result);
     371             :     }
     372             : 
     373             :     /*
     374             :      * Have returned all regular locks. Now start on the SIREAD predicate
     375             :      * locks.
     376             :      */
     377         574 :     predLockData = mystatus->predLockData;
     378         574 :     if (mystatus->predLockIdx < predLockData->nelements)
     379             :     {
     380             :         PredicateLockTargetType lockType;
     381             : 
     382           6 :         PREDICATELOCKTARGETTAG *predTag = &(predLockData->locktags[mystatus->predLockIdx]);
     383           6 :         SERIALIZABLEXACT *xact = &(predLockData->xacts[mystatus->predLockIdx]);
     384           6 :         Datum       values[NUM_LOCK_STATUS_COLUMNS] = {0};
     385           6 :         bool        nulls[NUM_LOCK_STATUS_COLUMNS] = {0};
     386             :         HeapTuple   tuple;
     387             :         Datum       result;
     388             : 
     389           6 :         mystatus->predLockIdx++;
     390             : 
     391             :         /*
     392             :          * Form tuple with appropriate data.
     393             :          */
     394             : 
     395             :         /* lock type */
     396           6 :         lockType = GET_PREDICATELOCKTARGETTAG_TYPE(*predTag);
     397             : 
     398           6 :         values[0] = CStringGetTextDatum(PredicateLockTagTypeNames[lockType]);
     399             : 
     400             :         /* lock target */
     401           6 :         values[1] = GET_PREDICATELOCKTARGETTAG_DB(*predTag);
     402           6 :         values[2] = GET_PREDICATELOCKTARGETTAG_RELATION(*predTag);
     403           6 :         if (lockType == PREDLOCKTAG_TUPLE)
     404           6 :             values[4] = GET_PREDICATELOCKTARGETTAG_OFFSET(*predTag);
     405             :         else
     406           0 :             nulls[4] = true;
     407           6 :         if ((lockType == PREDLOCKTAG_TUPLE) ||
     408             :             (lockType == PREDLOCKTAG_PAGE))
     409           6 :             values[3] = GET_PREDICATELOCKTARGETTAG_PAGE(*predTag);
     410             :         else
     411           0 :             nulls[3] = true;
     412             : 
     413             :         /* these fields are targets for other types of locks */
     414           6 :         nulls[5] = true;        /* virtualxid */
     415           6 :         nulls[6] = true;        /* transactionid */
     416           6 :         nulls[7] = true;        /* classid */
     417           6 :         nulls[8] = true;        /* objid */
     418           6 :         nulls[9] = true;        /* objsubid */
     419             : 
     420             :         /* lock holder */
     421           6 :         values[10] = VXIDGetDatum(xact->vxid.backendId,
     422             :                                   xact->vxid.localTransactionId);
     423           6 :         if (xact->pid != 0)
     424           6 :             values[11] = Int32GetDatum(xact->pid);
     425             :         else
     426           0 :             nulls[11] = true;
     427             : 
     428             :         /*
     429             :          * Lock mode. Currently all predicate locks are SIReadLocks, which are
     430             :          * always held (never waiting) and have no fast path
     431             :          */
     432           6 :         values[12] = CStringGetTextDatum("SIReadLock");
     433           6 :         values[13] = BoolGetDatum(true);
     434           6 :         values[14] = BoolGetDatum(false);
     435           6 :         nulls[15] = true;
     436             : 
     437           6 :         tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
     438           6 :         result = HeapTupleGetDatum(tuple);
     439           6 :         SRF_RETURN_NEXT(funcctx, result);
     440             :     }
     441             : 
     442         568 :     SRF_RETURN_DONE(funcctx);
     443             : }
     444             : 
     445             : 
     446             : /*
     447             :  * pg_blocking_pids - produce an array of the PIDs blocking given PID
     448             :  *
     449             :  * The reported PIDs are those that hold a lock conflicting with blocked_pid's
     450             :  * current request (hard block), or are requesting such a lock and are ahead
     451             :  * of blocked_pid in the lock's wait queue (soft block).
     452             :  *
     453             :  * In parallel-query cases, we report all PIDs blocking any member of the
     454             :  * given PID's lock group, and the reported PIDs are those of the blocking
     455             :  * PIDs' lock group leaders.  This allows callers to compare the result to
     456             :  * lists of clients' pg_backend_pid() results even during a parallel query.
     457             :  *
     458             :  * Parallel query makes it possible for there to be duplicate PIDs in the
     459             :  * result (either because multiple waiters are blocked by same PID, or
     460             :  * because multiple blockers have same group leader PID).  We do not bother
     461             :  * to eliminate such duplicates from the result.
     462             :  *
     463             :  * We need not consider predicate locks here, since those don't block anything.
     464             :  */
     465             : Datum
     466        6658 : pg_blocking_pids(PG_FUNCTION_ARGS)
     467             : {
     468        6658 :     int         blocked_pid = PG_GETARG_INT32(0);
     469             :     Datum      *arrayelems;
     470             :     int         narrayelems;
     471             :     BlockedProcsData *lockData; /* state data from lmgr */
     472             :     int         i,
     473             :                 j;
     474             : 
     475             :     /* Collect a snapshot of lock manager state */
     476        6658 :     lockData = GetBlockerStatusData(blocked_pid);
     477             : 
     478             :     /* We can't need more output entries than there are reported PROCLOCKs */
     479        6658 :     arrayelems = (Datum *) palloc(lockData->nlocks * sizeof(Datum));
     480        6658 :     narrayelems = 0;
     481             : 
     482             :     /* For each blocked proc in the lock group ... */
     483        8844 :     for (i = 0; i < lockData->nprocs; i++)
     484             :     {
     485        2186 :         BlockedProcData *bproc = &lockData->procs[i];
     486        2186 :         LockInstanceData *instances = &lockData->locks[bproc->first_lock];
     487        2186 :         int        *preceding_waiters = &lockData->waiter_pids[bproc->first_waiter];
     488             :         LockInstanceData *blocked_instance;
     489             :         LockMethod  lockMethodTable;
     490             :         int         conflictMask;
     491             : 
     492             :         /*
     493             :          * Locate the blocked proc's own entry in the LockInstanceData array.
     494             :          * There should be exactly one matching entry.
     495             :          */
     496        2186 :         blocked_instance = NULL;
     497        6648 :         for (j = 0; j < bproc->num_locks; j++)
     498             :         {
     499        4462 :             LockInstanceData *instance = &(instances[j]);
     500             : 
     501        4462 :             if (instance->pid == bproc->pid)
     502             :             {
     503             :                 Assert(blocked_instance == NULL);
     504        2186 :                 blocked_instance = instance;
     505             :             }
     506             :         }
     507             :         Assert(blocked_instance != NULL);
     508             : 
     509        2186 :         lockMethodTable = GetLockTagsMethodTable(&(blocked_instance->locktag));
     510        2186 :         conflictMask = lockMethodTable->conflictTab[blocked_instance->waitLockMode];
     511             : 
     512             :         /* Now scan the PROCLOCK data for conflicting procs */
     513        6648 :         for (j = 0; j < bproc->num_locks; j++)
     514             :         {
     515        4462 :             LockInstanceData *instance = &(instances[j]);
     516             : 
     517             :             /* A proc never blocks itself, so ignore that entry */
     518        4462 :             if (instance == blocked_instance)
     519        2186 :                 continue;
     520             :             /* Members of same lock group never block each other, either */
     521        2276 :             if (instance->leaderPid == blocked_instance->leaderPid)
     522           0 :                 continue;
     523             : 
     524        2276 :             if (conflictMask & instance->holdMask)
     525             :             {
     526             :                 /* hard block: blocked by lock already held by this entry */
     527             :             }
     528          88 :             else if (instance->waitLockMode != NoLock &&
     529          80 :                      (conflictMask & LOCKBIT_ON(instance->waitLockMode)))
     530          16 :             {
     531             :                 /* conflict in lock requests; who's in front in wait queue? */
     532          40 :                 bool        ahead = false;
     533             :                 int         k;
     534             : 
     535          40 :                 for (k = 0; k < bproc->num_waiters; k++)
     536             :                 {
     537          16 :                     if (preceding_waiters[k] == instance->pid)
     538             :                     {
     539             :                         /* soft block: this entry is ahead of blocked proc */
     540          16 :                         ahead = true;
     541          16 :                         break;
     542             :                     }
     543             :                 }
     544          40 :                 if (!ahead)
     545          24 :                     continue;   /* not blocked by this entry */
     546             :             }
     547             :             else
     548             :             {
     549             :                 /* not blocked by this entry */
     550          48 :                 continue;
     551             :             }
     552             : 
     553             :             /* blocked by this entry, so emit a record */
     554        2204 :             arrayelems[narrayelems++] = Int32GetDatum(instance->leaderPid);
     555             :         }
     556             :     }
     557             : 
     558             :     /* Assert we didn't overrun arrayelems[] */
     559             :     Assert(narrayelems <= lockData->nlocks);
     560             : 
     561        6658 :     PG_RETURN_ARRAYTYPE_P(construct_array_builtin(arrayelems, narrayelems, INT4OID));
     562             : }
     563             : 
     564             : 
     565             : /*
     566             :  * pg_safe_snapshot_blocking_pids - produce an array of the PIDs blocking
     567             :  * given PID from getting a safe snapshot
     568             :  *
     569             :  * XXX this does not consider parallel-query cases; not clear how big a
     570             :  * problem that is in practice
     571             :  */
     572             : Datum
     573           0 : pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
     574             : {
     575           0 :     int         blocked_pid = PG_GETARG_INT32(0);
     576             :     int        *blockers;
     577             :     int         num_blockers;
     578             :     Datum      *blocker_datums;
     579             : 
     580             :     /* A buffer big enough for any possible blocker list without truncation */
     581           0 :     blockers = (int *) palloc(MaxBackends * sizeof(int));
     582             : 
     583             :     /* Collect a snapshot of processes waited for by GetSafeSnapshot */
     584             :     num_blockers =
     585           0 :         GetSafeSnapshotBlockingPids(blocked_pid, blockers, MaxBackends);
     586             : 
     587             :     /* Convert int array to Datum array */
     588           0 :     if (num_blockers > 0)
     589             :     {
     590             :         int         i;
     591             : 
     592           0 :         blocker_datums = (Datum *) palloc(num_blockers * sizeof(Datum));
     593           0 :         for (i = 0; i < num_blockers; ++i)
     594           0 :             blocker_datums[i] = Int32GetDatum(blockers[i]);
     595             :     }
     596             :     else
     597           0 :         blocker_datums = NULL;
     598             : 
     599           0 :     PG_RETURN_ARRAYTYPE_P(construct_array_builtin(blocker_datums, num_blockers, INT4OID));
     600             : }
     601             : 
     602             : 
     603             : /*
     604             :  * pg_isolation_test_session_is_blocked - support function for isolationtester
     605             :  *
     606             :  * Check if specified PID is blocked by any of the PIDs listed in the second
     607             :  * argument.  Currently, this looks for blocking caused by waiting for
     608             :  * heavyweight locks or safe snapshots.  We ignore blockage caused by PIDs
     609             :  * not directly under the isolationtester's control, eg autovacuum.
     610             :  *
     611             :  * This is an undocumented function intended for use by the isolation tester,
     612             :  * and may change in future releases as required for testing purposes.
     613             :  */
     614             : Datum
     615        6658 : pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
     616             : {
     617        6658 :     int         blocked_pid = PG_GETARG_INT32(0);
     618        6658 :     ArrayType  *interesting_pids_a = PG_GETARG_ARRAYTYPE_P(1);
     619             :     ArrayType  *blocking_pids_a;
     620             :     int32      *interesting_pids;
     621             :     int32      *blocking_pids;
     622             :     int         num_interesting_pids;
     623             :     int         num_blocking_pids;
     624             :     int         dummy;
     625             :     int         i,
     626             :                 j;
     627             : 
     628             :     /* Validate the passed-in array */
     629             :     Assert(ARR_ELEMTYPE(interesting_pids_a) == INT4OID);
     630        6658 :     if (array_contains_nulls(interesting_pids_a))
     631           0 :         elog(ERROR, "array must not contain nulls");
     632        6658 :     interesting_pids = (int32 *) ARR_DATA_PTR(interesting_pids_a);
     633        6658 :     num_interesting_pids = ArrayGetNItems(ARR_NDIM(interesting_pids_a),
     634             :                                           ARR_DIMS(interesting_pids_a));
     635             : 
     636             :     /*
     637             :      * Get the PIDs of all sessions blocking the given session's attempt to
     638             :      * acquire heavyweight locks.
     639             :      */
     640             :     blocking_pids_a =
     641        6658 :         DatumGetArrayTypeP(DirectFunctionCall1(pg_blocking_pids, blocked_pid));
     642             : 
     643             :     Assert(ARR_ELEMTYPE(blocking_pids_a) == INT4OID);
     644             :     Assert(!array_contains_nulls(blocking_pids_a));
     645        6658 :     blocking_pids = (int32 *) ARR_DATA_PTR(blocking_pids_a);
     646        6658 :     num_blocking_pids = ArrayGetNItems(ARR_NDIM(blocking_pids_a),
     647             :                                        ARR_DIMS(blocking_pids_a));
     648             : 
     649             :     /*
     650             :      * Check if any of these are in the list of interesting PIDs, that being
     651             :      * the sessions that the isolation tester is running.  We don't use
     652             :      * "arrayoverlaps" here, because it would lead to cache lookups and one of
     653             :      * our goals is to run quickly with debug_discard_caches > 0.  We expect
     654             :      * blocking_pids to be usually empty and otherwise a very small number in
     655             :      * isolation tester cases, so make that the outer loop of a naive search
     656             :      * for a match.
     657             :      */
     658        6658 :     for (i = 0; i < num_blocking_pids; i++)
     659        3324 :         for (j = 0; j < num_interesting_pids; j++)
     660             :         {
     661        3324 :             if (blocking_pids[i] == interesting_pids[j])
     662        2186 :                 PG_RETURN_BOOL(true);
     663             :         }
     664             : 
     665             :     /*
     666             :      * Check if blocked_pid is waiting for a safe snapshot.  We could in
     667             :      * theory check the resulting array of blocker PIDs against the
     668             :      * interesting PIDs list, but since there is no danger of autovacuum
     669             :      * blocking GetSafeSnapshot there seems to be no point in expending cycles
     670             :      * on allocating a buffer and searching for overlap; so it's presently
     671             :      * sufficient for the isolation tester's purposes to use a single element
     672             :      * buffer and check if the number of safe snapshot blockers is non-zero.
     673             :      */
     674        4472 :     if (GetSafeSnapshotBlockingPids(blocked_pid, &dummy, 1) > 0)
     675           4 :         PG_RETURN_BOOL(true);
     676             : 
     677        4468 :     PG_RETURN_BOOL(false);
     678             : }
     679             : 
     680             : 
     681             : /*
     682             :  * Functions for manipulating advisory locks
     683             :  *
     684             :  * We make use of the locktag fields as follows:
     685             :  *
     686             :  *  field1: MyDatabaseId ... ensures locks are local to each database
     687             :  *  field2: first of 2 int4 keys, or high-order half of an int8 key
     688             :  *  field3: second of 2 int4 keys, or low-order half of an int8 key
     689             :  *  field4: 1 if using an int8 key, 2 if using 2 int4 keys
     690             :  */
     691             : #define SET_LOCKTAG_INT64(tag, key64) \
     692             :     SET_LOCKTAG_ADVISORY(tag, \
     693             :                          MyDatabaseId, \
     694             :                          (uint32) ((key64) >> 32), \
     695             :                          (uint32) (key64), \
     696             :                          1)
     697             : #define SET_LOCKTAG_INT32(tag, key1, key2) \
     698             :     SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, key1, key2, 2)
     699             : 
     700             : /*
     701             :  * pg_advisory_lock(int8) - acquire exclusive lock on an int8 key
     702             :  */
     703             : Datum
     704         424 : pg_advisory_lock_int8(PG_FUNCTION_ARGS)
     705             : {
     706         424 :     int64       key = PG_GETARG_INT64(0);
     707             :     LOCKTAG     tag;
     708             : 
     709         424 :     SET_LOCKTAG_INT64(tag, key);
     710             : 
     711         424 :     (void) LockAcquire(&tag, ExclusiveLock, true, false);
     712             : 
     713         422 :     PG_RETURN_VOID();
     714             : }
     715             : 
     716             : /*
     717             :  * pg_advisory_xact_lock(int8) - acquire xact scoped
     718             :  * exclusive lock on an int8 key
     719             :  */
     720             : Datum
     721          60 : pg_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
     722             : {
     723          60 :     int64       key = PG_GETARG_INT64(0);
     724             :     LOCKTAG     tag;
     725             : 
     726          60 :     SET_LOCKTAG_INT64(tag, key);
     727             : 
     728          60 :     (void) LockAcquire(&tag, ExclusiveLock, false, false);
     729             : 
     730          60 :     PG_RETURN_VOID();
     731             : }
     732             : 
     733             : /*
     734             :  * pg_advisory_lock_shared(int8) - acquire share lock on an int8 key
     735             :  */
     736             : Datum
     737          56 : pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
     738             : {
     739          56 :     int64       key = PG_GETARG_INT64(0);
     740             :     LOCKTAG     tag;
     741             : 
     742          56 :     SET_LOCKTAG_INT64(tag, key);
     743             : 
     744          56 :     (void) LockAcquire(&tag, ShareLock, true, false);
     745             : 
     746          56 :     PG_RETURN_VOID();
     747             : }
     748             : 
     749             : /*
     750             :  * pg_advisory_xact_lock_shared(int8) - acquire xact scoped
     751             :  * share lock on an int8 key
     752             :  */
     753             : Datum
     754       40042 : pg_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
     755             : {
     756       40042 :     int64       key = PG_GETARG_INT64(0);
     757             :     LOCKTAG     tag;
     758             : 
     759       40042 :     SET_LOCKTAG_INT64(tag, key);
     760             : 
     761       40042 :     (void) LockAcquire(&tag, ShareLock, false, false);
     762             : 
     763       40042 :     PG_RETURN_VOID();
     764             : }
     765             : 
     766             : /*
     767             :  * pg_try_advisory_lock(int8) - acquire exclusive lock on an int8 key, no wait
     768             :  *
     769             :  * Returns true if successful, false if lock not available
     770             :  */
     771             : Datum
     772         842 : pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
     773             : {
     774         842 :     int64       key = PG_GETARG_INT64(0);
     775             :     LOCKTAG     tag;
     776             :     LockAcquireResult res;
     777             : 
     778         842 :     SET_LOCKTAG_INT64(tag, key);
     779             : 
     780         842 :     res = LockAcquire(&tag, ExclusiveLock, true, true);
     781             : 
     782         842 :     PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
     783             : }
     784             : 
     785             : /*
     786             :  * pg_try_advisory_xact_lock(int8) - acquire xact scoped
     787             :  * exclusive lock on an int8 key, no wait
     788             :  *
     789             :  * Returns true if successful, false if lock not available
     790             :  */
     791             : Datum
     792           0 : pg_try_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
     793             : {
     794           0 :     int64       key = PG_GETARG_INT64(0);
     795             :     LOCKTAG     tag;
     796             :     LockAcquireResult res;
     797             : 
     798           0 :     SET_LOCKTAG_INT64(tag, key);
     799             : 
     800           0 :     res = LockAcquire(&tag, ExclusiveLock, false, true);
     801             : 
     802           0 :     PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
     803             : }
     804             : 
     805             : /*
     806             :  * pg_try_advisory_lock_shared(int8) - acquire share lock on an int8 key, no wait
     807             :  *
     808             :  * Returns true if successful, false if lock not available
     809             :  */
     810             : Datum
     811           0 : pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
     812             : {
     813           0 :     int64       key = PG_GETARG_INT64(0);
     814             :     LOCKTAG     tag;
     815             :     LockAcquireResult res;
     816             : 
     817           0 :     SET_LOCKTAG_INT64(tag, key);
     818             : 
     819           0 :     res = LockAcquire(&tag, ShareLock, true, true);
     820             : 
     821           0 :     PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
     822             : }
     823             : 
     824             : /*
     825             :  * pg_try_advisory_xact_lock_shared(int8) - acquire xact scoped
     826             :  * share lock on an int8 key, no wait
     827             :  *
     828             :  * Returns true if successful, false if lock not available
     829             :  */
     830             : Datum
     831           0 : pg_try_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
     832             : {
     833           0 :     int64       key = PG_GETARG_INT64(0);
     834             :     LOCKTAG     tag;
     835             :     LockAcquireResult res;
     836             : 
     837           0 :     SET_LOCKTAG_INT64(tag, key);
     838             : 
     839           0 :     res = LockAcquire(&tag, ShareLock, false, true);
     840             : 
     841           0 :     PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
     842             : }
     843             : 
     844             : /*
     845             :  * pg_advisory_unlock(int8) - release exclusive lock on an int8 key
     846             :  *
     847             :  * Returns true if successful, false if lock was not held
     848             : */
     849             : Datum
     850         282 : pg_advisory_unlock_int8(PG_FUNCTION_ARGS)
     851             : {
     852         282 :     int64       key = PG_GETARG_INT64(0);
     853             :     LOCKTAG     tag;
     854             :     bool        res;
     855             : 
     856         282 :     SET_LOCKTAG_INT64(tag, key);
     857             : 
     858         282 :     res = LockRelease(&tag, ExclusiveLock, true);
     859             : 
     860         282 :     PG_RETURN_BOOL(res);
     861             : }
     862             : 
     863             : /*
     864             :  * pg_advisory_unlock_shared(int8) - release share lock on an int8 key
     865             :  *
     866             :  * Returns true if successful, false if lock was not held
     867             :  */
     868             : Datum
     869          30 : pg_advisory_unlock_shared_int8(PG_FUNCTION_ARGS)
     870             : {
     871          30 :     int64       key = PG_GETARG_INT64(0);
     872             :     LOCKTAG     tag;
     873             :     bool        res;
     874             : 
     875          30 :     SET_LOCKTAG_INT64(tag, key);
     876             : 
     877          30 :     res = LockRelease(&tag, ShareLock, true);
     878             : 
     879          30 :     PG_RETURN_BOOL(res);
     880             : }
     881             : 
     882             : /*
     883             :  * pg_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys
     884             :  */
     885             : Datum
     886          98 : pg_advisory_lock_int4(PG_FUNCTION_ARGS)
     887             : {
     888          98 :     int32       key1 = PG_GETARG_INT32(0);
     889          98 :     int32       key2 = PG_GETARG_INT32(1);
     890             :     LOCKTAG     tag;
     891             : 
     892          98 :     SET_LOCKTAG_INT32(tag, key1, key2);
     893             : 
     894          98 :     (void) LockAcquire(&tag, ExclusiveLock, true, false);
     895             : 
     896          98 :     PG_RETURN_VOID();
     897             : }
     898             : 
     899             : /*
     900             :  * pg_advisory_xact_lock(int4, int4) - acquire xact scoped
     901             :  * exclusive lock on 2 int4 keys
     902             :  */
     903             : Datum
     904         100 : pg_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
     905             : {
     906         100 :     int32       key1 = PG_GETARG_INT32(0);
     907         100 :     int32       key2 = PG_GETARG_INT32(1);
     908             :     LOCKTAG     tag;
     909             : 
     910         100 :     SET_LOCKTAG_INT32(tag, key1, key2);
     911             : 
     912         100 :     (void) LockAcquire(&tag, ExclusiveLock, false, false);
     913             : 
     914         100 :     PG_RETURN_VOID();
     915             : }
     916             : 
     917             : /*
     918             :  * pg_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys
     919             :  */
     920             : Datum
     921          36 : pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
     922             : {
     923          36 :     int32       key1 = PG_GETARG_INT32(0);
     924          36 :     int32       key2 = PG_GETARG_INT32(1);
     925             :     LOCKTAG     tag;
     926             : 
     927          36 :     SET_LOCKTAG_INT32(tag, key1, key2);
     928             : 
     929          36 :     (void) LockAcquire(&tag, ShareLock, true, false);
     930             : 
     931          36 :     PG_RETURN_VOID();
     932             : }
     933             : 
     934             : /*
     935             :  * pg_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
     936             :  * share lock on 2 int4 keys
     937             :  */
     938             : Datum
     939          30 : pg_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
     940             : {
     941          30 :     int32       key1 = PG_GETARG_INT32(0);
     942          30 :     int32       key2 = PG_GETARG_INT32(1);
     943             :     LOCKTAG     tag;
     944             : 
     945          30 :     SET_LOCKTAG_INT32(tag, key1, key2);
     946             : 
     947          30 :     (void) LockAcquire(&tag, ShareLock, false, false);
     948             : 
     949          30 :     PG_RETURN_VOID();
     950             : }
     951             : 
     952             : /*
     953             :  * pg_try_advisory_lock(int4, int4) - acquire exclusive lock on 2 int4 keys, no wait
     954             :  *
     955             :  * Returns true if successful, false if lock not available
     956             :  */
     957             : Datum
     958           0 : pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
     959             : {
     960           0 :     int32       key1 = PG_GETARG_INT32(0);
     961           0 :     int32       key2 = PG_GETARG_INT32(1);
     962             :     LOCKTAG     tag;
     963             :     LockAcquireResult res;
     964             : 
     965           0 :     SET_LOCKTAG_INT32(tag, key1, key2);
     966             : 
     967           0 :     res = LockAcquire(&tag, ExclusiveLock, true, true);
     968             : 
     969           0 :     PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
     970             : }
     971             : 
     972             : /*
     973             :  * pg_try_advisory_xact_lock(int4, int4) - acquire xact scoped
     974             :  * exclusive lock on 2 int4 keys, no wait
     975             :  *
     976             :  * Returns true if successful, false if lock not available
     977             :  */
     978             : Datum
     979          66 : pg_try_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
     980             : {
     981          66 :     int32       key1 = PG_GETARG_INT32(0);
     982          66 :     int32       key2 = PG_GETARG_INT32(1);
     983             :     LOCKTAG     tag;
     984             :     LockAcquireResult res;
     985             : 
     986          66 :     SET_LOCKTAG_INT32(tag, key1, key2);
     987             : 
     988          66 :     res = LockAcquire(&tag, ExclusiveLock, false, true);
     989             : 
     990          66 :     PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
     991             : }
     992             : 
     993             : /*
     994             :  * pg_try_advisory_lock_shared(int4, int4) - acquire share lock on 2 int4 keys, no wait
     995             :  *
     996             :  * Returns true if successful, false if lock not available
     997             :  */
     998             : Datum
     999           0 : pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
    1000             : {
    1001           0 :     int32       key1 = PG_GETARG_INT32(0);
    1002           0 :     int32       key2 = PG_GETARG_INT32(1);
    1003             :     LOCKTAG     tag;
    1004             :     LockAcquireResult res;
    1005             : 
    1006           0 :     SET_LOCKTAG_INT32(tag, key1, key2);
    1007             : 
    1008           0 :     res = LockAcquire(&tag, ShareLock, true, true);
    1009             : 
    1010           0 :     PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
    1011             : }
    1012             : 
    1013             : /*
    1014             :  * pg_try_advisory_xact_lock_shared(int4, int4) - acquire xact scoped
    1015             :  * share lock on 2 int4 keys, no wait
    1016             :  *
    1017             :  * Returns true if successful, false if lock not available
    1018             :  */
    1019             : Datum
    1020           0 : pg_try_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
    1021             : {
    1022           0 :     int32       key1 = PG_GETARG_INT32(0);
    1023           0 :     int32       key2 = PG_GETARG_INT32(1);
    1024             :     LOCKTAG     tag;
    1025             :     LockAcquireResult res;
    1026             : 
    1027           0 :     SET_LOCKTAG_INT32(tag, key1, key2);
    1028             : 
    1029           0 :     res = LockAcquire(&tag, ShareLock, false, true);
    1030             : 
    1031           0 :     PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
    1032             : }
    1033             : 
    1034             : /*
    1035             :  * pg_advisory_unlock(int4, int4) - release exclusive lock on 2 int4 keys
    1036             :  *
    1037             :  * Returns true if successful, false if lock was not held
    1038             : */
    1039             : Datum
    1040          94 : pg_advisory_unlock_int4(PG_FUNCTION_ARGS)
    1041             : {
    1042          94 :     int32       key1 = PG_GETARG_INT32(0);
    1043          94 :     int32       key2 = PG_GETARG_INT32(1);
    1044             :     LOCKTAG     tag;
    1045             :     bool        res;
    1046             : 
    1047          94 :     SET_LOCKTAG_INT32(tag, key1, key2);
    1048             : 
    1049          94 :     res = LockRelease(&tag, ExclusiveLock, true);
    1050             : 
    1051          94 :     PG_RETURN_BOOL(res);
    1052             : }
    1053             : 
    1054             : /*
    1055             :  * pg_advisory_unlock_shared(int4, int4) - release share lock on 2 int4 keys
    1056             :  *
    1057             :  * Returns true if successful, false if lock was not held
    1058             :  */
    1059             : Datum
    1060          30 : pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
    1061             : {
    1062          30 :     int32       key1 = PG_GETARG_INT32(0);
    1063          30 :     int32       key2 = PG_GETARG_INT32(1);
    1064             :     LOCKTAG     tag;
    1065             :     bool        res;
    1066             : 
    1067          30 :     SET_LOCKTAG_INT32(tag, key1, key2);
    1068             : 
    1069          30 :     res = LockRelease(&tag, ShareLock, true);
    1070             : 
    1071          30 :     PG_RETURN_BOOL(res);
    1072             : }
    1073             : 
    1074             : /*
    1075             :  * pg_advisory_unlock_all() - release all advisory locks
    1076             :  */
    1077             : Datum
    1078         238 : pg_advisory_unlock_all(PG_FUNCTION_ARGS)
    1079             : {
    1080         238 :     LockReleaseSession(USER_LOCKMETHOD);
    1081             : 
    1082         238 :     PG_RETURN_VOID();
    1083             : }

Generated by: LCOV version 1.14