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-02-17 17:20:33 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/varlena.h"
      44              : 
      45            2 : PG_MODULE_MAGIC_EXT(
      46              :                     .name = "pgrowlocks",
      47              :                     .version = PG_VERSION
      48              : );
      49              : 
      50            2 : PG_FUNCTION_INFO_V1(pgrowlocks);
      51              : 
      52              : /* ----------
      53              :  * pgrowlocks:
      54              :  * returns tids of rows being locked
      55              :  * ----------
      56              :  */
      57              : 
      58              : #define NCHARS 32
      59              : 
      60              : #define     Atnum_tid       0
      61              : #define     Atnum_xmax      1
      62              : #define     Atnum_ismulti   2
      63              : #define     Atnum_xids      3
      64              : #define     Atnum_modes     4
      65              : #define     Atnum_pids      5
      66              : 
      67              : Datum
      68           12 : pgrowlocks(PG_FUNCTION_ARGS)
      69              : {
      70           12 :     text       *relname = PG_GETARG_TEXT_PP(0);
      71           12 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
      72              :     AttInMetadata *attinmeta;
      73              :     Relation    rel;
      74              :     RangeVar   *relrv;
      75              :     TableScanDesc scan;
      76              :     HeapScanDesc hscan;
      77              :     HeapTuple   tuple;
      78              :     AclResult   aclresult;
      79              :     char      **values;
      80              : 
      81           12 :     InitMaterializedSRF(fcinfo, 0);
      82              : 
      83              :     /* Access the table */
      84           12 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
      85           12 :     rel = relation_openrv(relrv, AccessShareLock);
      86              : 
      87           12 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
      88            0 :         ereport(ERROR,
      89              :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
      90              :                  errmsg("\"%s\" is a partitioned table",
      91              :                         RelationGetRelationName(rel)),
      92              :                  errdetail("Partitioned tables do not contain rows.")));
      93           12 :     else if (rel->rd_rel->relkind != RELKIND_RELATION)
      94            0 :         ereport(ERROR,
      95              :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
      96              :                  errmsg("\"%s\" is not a table",
      97              :                         RelationGetRelationName(rel))));
      98           12 :     else if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
      99            0 :         ereport(ERROR,
     100              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     101              :                  errmsg("only heap AM is supported")));
     102              : 
     103              :     /*
     104              :      * check permissions: must have SELECT on table or be in
     105              :      * pg_stat_scan_tables
     106              :      */
     107           12 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
     108              :                                   ACL_SELECT);
     109           12 :     if (aclresult != ACLCHECK_OK)
     110            0 :         aclresult = has_privs_of_role(GetUserId(), ROLE_PG_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
     111              : 
     112           12 :     if (aclresult != ACLCHECK_OK)
     113            0 :         aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
     114            0 :                        RelationGetRelationName(rel));
     115              : 
     116              :     /* Scan the relation */
     117           12 :     scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
     118           12 :     hscan = (HeapScanDesc) scan;
     119              : 
     120           12 :     attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
     121              : 
     122           12 :     values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
     123              : 
     124           36 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
     125              :     {
     126              :         TM_Result   htsu;
     127              :         TransactionId xmax;
     128              :         uint16      infomask;
     129              : 
     130              :         /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
     131           24 :         LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
     132              : 
     133           24 :         htsu = HeapTupleSatisfiesUpdate(tuple,
     134              :                                         GetCurrentCommandId(false),
     135              :                                         hscan->rs_cbuf);
     136           24 :         xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
     137           24 :         infomask = tuple->t_data->t_infomask;
     138              : 
     139              :         /*
     140              :          * A tuple is locked if HTSU returns BeingModified.
     141              :          */
     142           24 :         if (htsu == TM_BeingModified)
     143              :         {
     144           22 :             values[Atnum_tid] = DatumGetCString(DirectFunctionCall1(tidout,
     145              :                                                                     PointerGetDatum(&tuple->t_self)));
     146              : 
     147           22 :             values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
     148           22 :             snprintf(values[Atnum_xmax], NCHARS, "%u", xmax);
     149           22 :             if (infomask & HEAP_XMAX_IS_MULTI)
     150              :             {
     151              :                 MultiXactMember *members;
     152              :                 int         nmembers;
     153            8 :                 bool        first = true;
     154              :                 bool        allow_old;
     155              : 
     156            8 :                 values[Atnum_ismulti] = pstrdup("true");
     157              : 
     158            8 :                 allow_old = HEAP_LOCKED_UPGRADED(infomask);
     159            8 :                 nmembers = GetMultiXactIdMembers(xmax, &members, allow_old,
     160              :                                                  false);
     161            8 :                 if (nmembers == -1)
     162              :                 {
     163            0 :                     values[Atnum_xids] = "{0}";
     164            0 :                     values[Atnum_modes] = "{transient upgrade status}";
     165            0 :                     values[Atnum_pids] = "{0}";
     166              :                 }
     167              :                 else
     168              :                 {
     169              :                     int         j;
     170              : 
     171            8 :                     values[Atnum_xids] = palloc(NCHARS * nmembers);
     172            8 :                     values[Atnum_modes] = palloc(NCHARS * nmembers);
     173            8 :                     values[Atnum_pids] = palloc(NCHARS * nmembers);
     174              : 
     175            8 :                     strcpy(values[Atnum_xids], "{");
     176            8 :                     strcpy(values[Atnum_modes], "{");
     177            8 :                     strcpy(values[Atnum_pids], "{");
     178              : 
     179           24 :                     for (j = 0; j < nmembers; j++)
     180              :                     {
     181              :                         char        buf[NCHARS];
     182              : 
     183           16 :                         if (!first)
     184              :                         {
     185            8 :                             strcat(values[Atnum_xids], ",");
     186            8 :                             strcat(values[Atnum_modes], ",");
     187            8 :                             strcat(values[Atnum_pids], ",");
     188              :                         }
     189           16 :                         snprintf(buf, NCHARS, "%u", members[j].xid);
     190           16 :                         strcat(values[Atnum_xids], buf);
     191           16 :                         switch (members[j].status)
     192              :                         {
     193            1 :                             case MultiXactStatusUpdate:
     194            1 :                                 snprintf(buf, NCHARS, "Update");
     195            1 :                                 break;
     196            1 :                             case MultiXactStatusNoKeyUpdate:
     197            1 :                                 snprintf(buf, NCHARS, "No Key Update");
     198            1 :                                 break;
     199            2 :                             case MultiXactStatusForUpdate:
     200            2 :                                 snprintf(buf, NCHARS, "For Update");
     201            2 :                                 break;
     202            2 :                             case MultiXactStatusForNoKeyUpdate:
     203            2 :                                 snprintf(buf, NCHARS, "For No Key Update");
     204            2 :                                 break;
     205            2 :                             case MultiXactStatusForShare:
     206            2 :                                 snprintf(buf, NCHARS, "For Share");
     207            2 :                                 break;
     208            8 :                             case MultiXactStatusForKeyShare:
     209            8 :                                 snprintf(buf, NCHARS, "For Key Share");
     210            8 :                                 break;
     211              :                         }
     212           16 :                         strcat(values[Atnum_modes], buf);
     213           16 :                         snprintf(buf, NCHARS, "%d",
     214           16 :                                  BackendXidGetPid(members[j].xid));
     215           16 :                         strcat(values[Atnum_pids], buf);
     216              : 
     217           16 :                         first = false;
     218              :                     }
     219              : 
     220            8 :                     strcat(values[Atnum_xids], "}");
     221            8 :                     strcat(values[Atnum_modes], "}");
     222            8 :                     strcat(values[Atnum_pids], "}");
     223              :                 }
     224              :             }
     225              :             else
     226              :             {
     227           14 :                 values[Atnum_ismulti] = pstrdup("false");
     228              : 
     229           14 :                 values[Atnum_xids] = palloc(NCHARS * sizeof(char));
     230           14 :                 snprintf(values[Atnum_xids], NCHARS, "{%u}", xmax);
     231              : 
     232           14 :                 values[Atnum_modes] = palloc(NCHARS);
     233           14 :                 if (infomask & HEAP_XMAX_LOCK_ONLY)
     234              :                 {
     235           12 :                     if (HEAP_XMAX_IS_SHR_LOCKED(infomask))
     236            2 :                         snprintf(values[Atnum_modes], NCHARS, "{For Share}");
     237           10 :                     else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
     238            6 :                         snprintf(values[Atnum_modes], NCHARS, "{For Key Share}");
     239            4 :                     else if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
     240              :                     {
     241            4 :                         if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
     242            2 :                             snprintf(values[Atnum_modes], NCHARS, "{For Update}");
     243              :                         else
     244            2 :                             snprintf(values[Atnum_modes], NCHARS, "{For No Key Update}");
     245              :                     }
     246              :                     else
     247              :                         /* neither keyshare nor exclusive bit it set */
     248            0 :                         snprintf(values[Atnum_modes], NCHARS,
     249              :                                  "{transient upgrade status}");
     250              :                 }
     251              :                 else
     252              :                 {
     253            2 :                     if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED)
     254            1 :                         snprintf(values[Atnum_modes], NCHARS, "{Update}");
     255              :                     else
     256            1 :                         snprintf(values[Atnum_modes], NCHARS, "{No Key Update}");
     257              :                 }
     258              : 
     259           14 :                 values[Atnum_pids] = palloc(NCHARS * sizeof(char));
     260           14 :                 snprintf(values[Atnum_pids], NCHARS, "{%d}",
     261              :                          BackendXidGetPid(xmax));
     262              :             }
     263              : 
     264           22 :             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
     265              : 
     266              :             /* build a tuple */
     267           22 :             tuple = BuildTupleFromCStrings(attinmeta, values);
     268           22 :             tuplestore_puttuple(rsinfo->setResult, tuple);
     269              :         }
     270              :         else
     271              :         {
     272            2 :             LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_UNLOCK);
     273              :         }
     274              :     }
     275              : 
     276           12 :     table_endscan(scan);
     277           12 :     table_close(rel, AccessShareLock);
     278           12 :     return (Datum) 0;
     279              : }
        

Generated by: LCOV version 2.0-1