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

Generated by: LCOV version 1.14