LCOV - code coverage report
Current view: top level - contrib/pgrowlocks - pgrowlocks.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 90.7 % 108 98
Test Date: 2026-07-25 22:15:46 Functions: 100.0 % 3 3
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 70.2 % 47 33

             Branch data     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