LCOV - code coverage report
Current view: top level - contrib/pgrowlocks - pgrowlocks.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.7 % 108 98
Test Date: 2026-04-07 14:16:30 Functions: 100.0 % 3 3
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * contrib/pgrowlocks/pgrowlocks.c
       3              :  *
       4              :  * Copyright (c) 2005-2006  Tatsuo Ishii
       5              :  *
       6              :  * Permission to use, copy, modify, and distribute this software and
       7              :  * its documentation for any purpose, without fee, and without a
       8              :  * written agreement is hereby granted, provided that the above
       9              :  * copyright notice and this paragraph and the following two
      10              :  * paragraphs appear in all copies.
      11              :  *
      12              :  * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
      13              :  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
      14              :  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
      15              :  * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
      16              :  * OF THE POSSIBILITY OF SUCH DAMAGE.
      17              :  *
      18              :  * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
      19              :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      20              :  * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
      21              :  * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
      22              :  * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
      23              :  */
      24              : 
      25              : #include "postgres.h"
      26              : 
      27              : #include "access/heapam.h"
      28              : #include "access/multixact.h"
      29              : #include "access/relscan.h"
      30              : #include "access/tableam.h"
      31              : #include "access/xact.h"
      32              : #include "catalog/namespace.h"
      33              : #include "catalog/pg_am_d.h"
      34              : #include "catalog/pg_authid.h"
      35              : #include "funcapi.h"
      36              : #include "miscadmin.h"
      37              : #include "storage/bufmgr.h"
      38              : #include "storage/procarray.h"
      39              : #include "utils/acl.h"
      40              : #include "utils/fmgrprotos.h"
      41              : #include "utils/rel.h"
      42              : #include "utils/snapmgr.h"
      43              : #include "utils/tuplestore.h"
      44              : #include "utils/varlena.h"
      45              : 
      46            2 : PG_MODULE_MAGIC_EXT(
      47              :                     .name = "pgrowlocks",
      48              :                     .version = PG_VERSION
      49              : );
      50              : 
      51            2 : PG_FUNCTION_INFO_V1(pgrowlocks);
      52              : 
      53              : /* ----------
      54              :  * pgrowlocks:
      55              :  * returns tids of rows being locked
      56              :  * ----------
      57              :  */
      58              : 
      59              : #define NCHARS 32
      60              : 
      61              : #define     Atnum_tid       0
      62              : #define     Atnum_xmax      1
      63              : #define     Atnum_ismulti   2
      64              : #define     Atnum_xids      3
      65              : #define     Atnum_modes     4
      66              : #define     Atnum_pids      5
      67              : 
      68              : Datum
      69           12 : pgrowlocks(PG_FUNCTION_ARGS)
      70              : {
      71           12 :     text       *relname = PG_GETARG_TEXT_PP(0);
      72           12 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
      73              :     AttInMetadata *attinmeta;
      74              :     Relation    rel;
      75              :     RangeVar   *relrv;
      76              :     TableScanDesc scan;
      77              :     HeapScanDesc hscan;
      78              :     HeapTuple   tuple;
      79              :     AclResult   aclresult;
      80              :     char      **values;
      81              : 
      82           12 :     InitMaterializedSRF(fcinfo, 0);
      83              : 
      84              :     /* Access the table */
      85           12 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
      86           12 :     rel = relation_openrv(relrv, AccessShareLock);
      87              : 
      88           12 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
      89            0 :         ereport(ERROR,
      90              :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
      91              :                  errmsg("\"%s\" is a partitioned table",
      92              :                         RelationGetRelationName(rel)),
      93              :                  errdetail("Partitioned tables do not contain rows.")));
      94           12 :     else if (rel->rd_rel->relkind != RELKIND_RELATION)
      95            0 :         ereport(ERROR,
      96              :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
      97              :                  errmsg("\"%s\" is not a table",
      98              :                         RelationGetRelationName(rel))));
      99           12 :     else if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
     100            0 :         ereport(ERROR,
     101              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     102              :                  errmsg("only heap AM is supported")));
     103              : 
     104              :     /*
     105              :      * check permissions: must have SELECT on table or be in
     106              :      * pg_stat_scan_tables
     107              :      */
     108           12 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
     109              :                                   ACL_SELECT);
     110           12 :     if (aclresult != ACLCHECK_OK)
     111            0 :         aclresult = has_privs_of_role(GetUserId(), ROLE_PG_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
     112              : 
     113           12 :     if (aclresult != ACLCHECK_OK)
     114            0 :         aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
     115            0 :                        RelationGetRelationName(rel));
     116              : 
     117              :     /* Scan the relation */
     118           12 :     scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL, SO_NONE);
     119           12 :     hscan = (HeapScanDesc) scan;
     120              : 
     121           12 :     attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
     122              : 
     123           12 :     values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
     124              : 
     125           36 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
     126              :     {
     127              :         TM_Result   htsu;
     128              :         TransactionId xmax;
     129              :         uint16      infomask;
     130              : 
     131              :         /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
     132           24 :         LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
     133              : 
     134           24 :         htsu = HeapTupleSatisfiesUpdate(tuple,
     135              :                                         GetCurrentCommandId(false),
     136              :                                         hscan->rs_cbuf);
     137           24 :         xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
     138           24 :         infomask = tuple->t_data->t_infomask;
     139              : 
     140              :         /*
     141              :          * A tuple is locked if HTSU returns BeingModified.
     142              :          */
     143           24 :         if (htsu == TM_BeingModified)
     144              :         {
     145           22 :             values[Atnum_tid] = DatumGetCString(DirectFunctionCall1(tidout,
     146              :                                                                     PointerGetDatum(&tuple->t_self)));
     147              : 
     148           22 :             values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
     149           22 :             snprintf(values[Atnum_xmax], NCHARS, "%u", xmax);
     150           22 :             if (infomask & HEAP_XMAX_IS_MULTI)
     151              :             {
     152              :                 MultiXactMember *members;
     153              :                 int         nmembers;
     154            8 :                 bool        first = true;
     155              :                 bool        allow_old;
     156              : 
     157            8 :                 values[Atnum_ismulti] = pstrdup("true");
     158              : 
     159            8 :                 allow_old = HEAP_LOCKED_UPGRADED(infomask);
     160            8 :                 nmembers = GetMultiXactIdMembers(xmax, &members, allow_old,
     161              :                                                  false);
     162            8 :                 if (nmembers == -1)
     163              :                 {
     164            0 :                     values[Atnum_xids] = "{0}";
     165            0 :                     values[Atnum_modes] = "{transient upgrade status}";
     166            0 :                     values[Atnum_pids] = "{0}";
     167              :                 }
     168              :                 else
     169              :                 {
     170              :                     int         j;
     171              : 
     172            8 :                     values[Atnum_xids] = palloc(NCHARS * nmembers);
     173            8 :                     values[Atnum_modes] = palloc(NCHARS * nmembers);
     174            8 :                     values[Atnum_pids] = palloc(NCHARS * nmembers);
     175              : 
     176            8 :                     strcpy(values[Atnum_xids], "{");
     177            8 :                     strcpy(values[Atnum_modes], "{");
     178            8 :                     strcpy(values[Atnum_pids], "{");
     179              : 
     180           24 :                     for (j = 0; j < nmembers; j++)
     181              :                     {
     182              :                         char        buf[NCHARS];
     183              : 
     184           16 :                         if (!first)
     185              :                         {
     186            8 :                             strcat(values[Atnum_xids], ",");
     187            8 :                             strcat(values[Atnum_modes], ",");
     188            8 :                             strcat(values[Atnum_pids], ",");
     189              :                         }
     190           16 :                         snprintf(buf, NCHARS, "%u", members[j].xid);
     191           16 :                         strcat(values[Atnum_xids], buf);
     192           16 :                         switch (members[j].status)
     193              :                         {
     194            1 :                             case MultiXactStatusUpdate:
     195            1 :                                 snprintf(buf, NCHARS, "Update");
     196            1 :                                 break;
     197            1 :                             case MultiXactStatusNoKeyUpdate:
     198            1 :                                 snprintf(buf, NCHARS, "No Key Update");
     199            1 :                                 break;
     200            2 :                             case MultiXactStatusForUpdate:
     201            2 :                                 snprintf(buf, NCHARS, "For Update");
     202            2 :                                 break;
     203            2 :                             case MultiXactStatusForNoKeyUpdate:
     204            2 :                                 snprintf(buf, NCHARS, "For No Key Update");
     205            2 :                                 break;
     206            2 :                             case MultiXactStatusForShare:
     207            2 :                                 snprintf(buf, NCHARS, "For Share");
     208            2 :                                 break;
     209            8 :                             case MultiXactStatusForKeyShare:
     210            8 :                                 snprintf(buf, NCHARS, "For Key Share");
     211            8 :                                 break;
     212              :                         }
     213           16 :                         strcat(values[Atnum_modes], buf);
     214           16 :                         snprintf(buf, NCHARS, "%d",
     215           16 :                                  BackendXidGetPid(members[j].xid));
     216           16 :                         strcat(values[Atnum_pids], buf);
     217              : 
     218           16 :                         first = false;
     219              :                     }
     220              : 
     221            8 :                     strcat(values[Atnum_xids], "}");
     222            8 :                     strcat(values[Atnum_modes], "}");
     223            8 :                     strcat(values[Atnum_pids], "}");
     224              :                 }
     225              :             }
     226              :             else
     227              :             {
     228           14 :                 values[Atnum_ismulti] = pstrdup("false");
     229              : 
     230           14 :                 values[Atnum_xids] = palloc(NCHARS * sizeof(char));
     231           14 :                 snprintf(values[Atnum_xids], NCHARS, "{%u}", xmax);
     232              : 
     233           14 :                 values[Atnum_modes] = palloc(NCHARS);
     234           14 :                 if (infomask & HEAP_XMAX_LOCK_ONLY)
     235              :                 {
     236           12 :                     if (HEAP_XMAX_IS_SHR_LOCKED(infomask))
     237            2 :                         snprintf(values[Atnum_modes], NCHARS, "{For Share}");
     238           10 :                     else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
     239            6 :                         snprintf(values[Atnum_modes], NCHARS, "{For Key Share}");
     240            4 :                     else if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
     241              :                     {
     242            4 :                         if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
     243            2 :                             snprintf(values[Atnum_modes], NCHARS, "{For Update}");
     244              :                         else
     245            2 :                             snprintf(values[Atnum_modes], NCHARS, "{For No Key Update}");
     246              :                     }
     247              :                     else
     248              :                         /* neither keyshare nor exclusive bit it set */
     249            0 :                         snprintf(values[Atnum_modes], NCHARS,
     250              :                                  "{transient upgrade status}");
     251              :                 }
     252              :                 else
     253              :                 {
     254            2 :                     if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
     255            1 :                         snprintf(values[Atnum_modes], NCHARS, "{Update}");
     256              :                     else
     257            1 :                         snprintf(values[Atnum_modes], NCHARS, "{No Key Update}");
     258              :                 }
     259              : 
     260           14 :                 values[Atnum_pids] = palloc(NCHARS * sizeof(char));
     261           14 :                 snprintf(values[Atnum_pids], NCHARS, "{%d}",
     262              :                          BackendXidGetPid(xmax));
     263              :             }
     264              : 
     265           22 :             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
     266              : 
     267              :             /* build a tuple */
     268           22 :             tuple = BuildTupleFromCStrings(attinmeta, values);
     269           22 :             tuplestore_puttuple(rsinfo->setResult, tuple);
     270              :         }
     271              :         else
     272              :         {
     273            2 :             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
     274              :         }
     275              :     }
     276              : 
     277           12 :     table_endscan(scan);
     278           12 :     table_close(rel, AccessShareLock);
     279           12 :     return (Datum) 0;
     280              : }
        

Generated by: LCOV version 2.0-1