LCOV - differential code coverage report
Current view: top level - contrib/pageinspect - heapfuncs.c (source / functions) Coverage Total Hit UNC UBC GIC GNC CBC ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 88.7 % 239 212 1 26 13 5 194 13 1 5
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 9 9 3 6
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * heapfuncs.c
       4                 :  *    Functions to investigate heap pages
       5                 :  *
       6                 :  * We check the input to these functions for corrupt pointers etc. that
       7                 :  * might cause crashes, but at the same time we try to print out as much
       8                 :  * information as possible, even if it's nonsense. That's because if a
       9                 :  * page is corrupt, we don't know why and how exactly it is corrupt, so we
      10                 :  * let the user judge it.
      11                 :  *
      12                 :  * These functions are restricted to superusers for the fear of introducing
      13                 :  * security holes if the input checking isn't as water-tight as it should be.
      14                 :  * You'd need to be superuser to obtain a raw page image anyway, so
      15                 :  * there's hardly any use case for using these without superuser-rights
      16                 :  * anyway.
      17                 :  *
      18                 :  * Copyright (c) 2007-2023, PostgreSQL Global Development Group
      19                 :  *
      20                 :  * IDENTIFICATION
      21                 :  *    contrib/pageinspect/heapfuncs.c
      22                 :  *
      23                 :  *-------------------------------------------------------------------------
      24                 :  */
      25                 : 
      26                 : #include "postgres.h"
      27                 : 
      28                 : #include "access/htup_details.h"
      29                 : #include "access/relation.h"
      30                 : #include "catalog/pg_am_d.h"
      31                 : #include "catalog/pg_type.h"
      32                 : #include "funcapi.h"
      33                 : #include "mb/pg_wchar.h"
      34                 : #include "miscadmin.h"
      35                 : #include "pageinspect.h"
      36                 : #include "port/pg_bitutils.h"
      37                 : #include "utils/array.h"
      38                 : #include "utils/builtins.h"
      39                 : #include "utils/rel.h"
      40                 : 
      41                 : /*
      42                 :  * It's not supported to create tuples with oids anymore, but when pg_upgrade
      43                 :  * was used to upgrade from an older version, tuples might still have an
      44                 :  * oid. Seems worthwhile to display that.
      45                 :  */
      46                 : #define HeapTupleHeaderGetOidOld(tup) \
      47                 : ( \
      48                 :     ((tup)->t_infomask & HEAP_HASOID_OLD) ? \
      49                 :        *((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) \
      50                 :     : \
      51                 :         InvalidOid \
      52                 : )
      53                 : 
      54                 : 
      55                 : /*
      56                 :  * bits_to_text
      57                 :  *
      58                 :  * Converts a bits8-array of 'len' bits to a human-readable
      59                 :  * c-string representation.
      60                 :  */
      61                 : static char *
      62 CBC           2 : bits_to_text(bits8 *bits, int len)
      63                 : {
      64                 :     int         i;
      65                 :     char       *str;
      66                 : 
      67               2 :     str = palloc(len + 1);
      68                 : 
      69              18 :     for (i = 0; i < len; i++)
      70              16 :         str[i] = (bits[(i / 8)] & (1 << (i % 8))) ? '1' : '0';
      71                 : 
      72               2 :     str[i] = '\0';
      73                 : 
      74               2 :     return str;
      75                 : }
      76                 : 
      77                 : 
      78                 : /*
      79                 :  * text_to_bits
      80                 :  *
      81                 :  * Converts a c-string representation of bits into a bits8-array. This is
      82                 :  * the reverse operation of previous routine.
      83                 :  */
      84                 : static bits8 *
      85               1 : text_to_bits(char *str, int len)
      86                 : {
      87                 :     bits8      *bits;
      88               1 :     int         off = 0;
      89               1 :     char        byte = 0;
      90                 : 
      91               1 :     bits = palloc(len + 1);
      92                 : 
      93               9 :     while (off < len)
      94                 :     {
      95               8 :         if (off % 8 == 0)
      96               1 :             byte = 0;
      97                 : 
      98               8 :         if ((str[off] == '0') || (str[off] == '1'))
      99               8 :             byte = byte | ((str[off] - '0') << off % 8);
     100                 :         else
     101 UBC           0 :             ereport(ERROR,
     102                 :                     (errcode(ERRCODE_DATA_CORRUPTED),
     103                 :                      errmsg("invalid character \"%.*s\" in t_bits string",
     104                 :                             pg_mblen(str + off), str + off)));
     105                 : 
     106 CBC           8 :         if (off % 8 == 7)
     107               1 :             bits[off / 8] = byte;
     108                 : 
     109               8 :         off++;
     110                 :     }
     111                 : 
     112               1 :     return bits;
     113                 : }
     114                 : 
     115                 : /*
     116                 :  * heap_page_items
     117                 :  *
     118                 :  * Allows inspection of line pointers and tuple headers of a heap page.
     119                 :  */
     120               8 : PG_FUNCTION_INFO_V1(heap_page_items);
     121                 : 
     122                 : typedef struct heap_page_items_state
     123                 : {
     124                 :     TupleDesc   tupd;
     125                 :     Page        page;
     126                 :     uint16      offset;
     127                 : } heap_page_items_state;
     128                 : 
     129                 : Datum
     130              53 : heap_page_items(PG_FUNCTION_ARGS)
     131                 : {
     132              53 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     133              53 :     heap_page_items_state *inter_call_data = NULL;
     134                 :     FuncCallContext *fctx;
     135                 :     int         raw_page_size;
     136                 : 
     137              53 :     if (!superuser())
     138 UBC           0 :         ereport(ERROR,
     139                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     140                 :                  errmsg("must be superuser to use raw page functions")));
     141                 : 
     142 CBC          53 :     raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
     143                 : 
     144              53 :     if (SRF_IS_FIRSTCALL())
     145                 :     {
     146                 :         TupleDesc   tupdesc;
     147                 :         MemoryContext mctx;
     148                 : 
     149               5 :         if (raw_page_size < SizeOfPageHeaderData)
     150 UBC           0 :             ereport(ERROR,
     151                 :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     152                 :                      errmsg("input page too small (%d bytes)", raw_page_size)));
     153                 : 
     154 CBC           5 :         fctx = SRF_FIRSTCALL_INIT();
     155               5 :         mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
     156                 : 
     157               5 :         inter_call_data = palloc(sizeof(heap_page_items_state));
     158                 : 
     159                 :         /* Build a tuple descriptor for our result type */
     160               5 :         if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     161 UBC           0 :             elog(ERROR, "return type must be a row type");
     162                 : 
     163 CBC           5 :         inter_call_data->tupd = tupdesc;
     164                 : 
     165               5 :         inter_call_data->offset = FirstOffsetNumber;
     166               5 :         inter_call_data->page = VARDATA(raw_page);
     167                 : 
     168               5 :         fctx->max_calls = PageGetMaxOffsetNumber(inter_call_data->page);
     169               5 :         fctx->user_fctx = inter_call_data;
     170                 : 
     171               5 :         MemoryContextSwitchTo(mctx);
     172                 :     }
     173                 : 
     174              53 :     fctx = SRF_PERCALL_SETUP();
     175              53 :     inter_call_data = fctx->user_fctx;
     176                 : 
     177              53 :     if (fctx->call_cntr < fctx->max_calls)
     178                 :     {
     179              48 :         Page        page = inter_call_data->page;
     180                 :         HeapTuple   resultTuple;
     181                 :         Datum       result;
     182                 :         ItemId      id;
     183                 :         Datum       values[14];
     184                 :         bool        nulls[14];
     185                 :         uint16      lp_offset;
     186                 :         uint16      lp_flags;
     187                 :         uint16      lp_len;
     188                 : 
     189              48 :         memset(nulls, 0, sizeof(nulls));
     190                 : 
     191                 :         /* Extract information from the line pointer */
     192                 : 
     193              48 :         id = PageGetItemId(page, inter_call_data->offset);
     194                 : 
     195              48 :         lp_offset = ItemIdGetOffset(id);
     196              48 :         lp_flags = ItemIdGetFlags(id);
     197              48 :         lp_len = ItemIdGetLength(id);
     198                 : 
     199              48 :         values[0] = UInt16GetDatum(inter_call_data->offset);
     200              48 :         values[1] = UInt16GetDatum(lp_offset);
     201              48 :         values[2] = UInt16GetDatum(lp_flags);
     202              48 :         values[3] = UInt16GetDatum(lp_len);
     203                 : 
     204                 :         /*
     205                 :          * We do just enough validity checking to make sure we don't reference
     206                 :          * data outside the page passed to us. The page could be corrupt in
     207                 :          * many other ways, but at least we won't crash.
     208                 :          */
     209              48 :         if (ItemIdHasStorage(id) &&
     210              43 :             lp_len >= MinHeapTupleSize &&
     211              43 :             lp_offset == MAXALIGN(lp_offset) &&
     212              43 :             lp_offset + lp_len <= raw_page_size)
     213              43 :         {
     214                 :             HeapTupleHeader tuphdr;
     215                 :             bytea      *tuple_data_bytea;
     216                 :             int         tuple_data_len;
     217                 : 
     218                 :             /* Extract information from the tuple header */
     219                 : 
     220              43 :             tuphdr = (HeapTupleHeader) PageGetItem(page, id);
     221                 : 
     222              43 :             values[4] = UInt32GetDatum(HeapTupleHeaderGetRawXmin(tuphdr));
     223              43 :             values[5] = UInt32GetDatum(HeapTupleHeaderGetRawXmax(tuphdr));
     224                 :             /* shared with xvac */
     225              43 :             values[6] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr));
     226              43 :             values[7] = PointerGetDatum(&tuphdr->t_ctid);
     227              43 :             values[8] = UInt32GetDatum(tuphdr->t_infomask2);
     228              43 :             values[9] = UInt32GetDatum(tuphdr->t_infomask);
     229              43 :             values[10] = UInt8GetDatum(tuphdr->t_hoff);
     230                 : 
     231                 :             /* Copy raw tuple data into bytea attribute */
     232              43 :             tuple_data_len = lp_len - tuphdr->t_hoff;
     233              43 :             tuple_data_bytea = (bytea *) palloc(tuple_data_len + VARHDRSZ);
     234              43 :             SET_VARSIZE(tuple_data_bytea, tuple_data_len + VARHDRSZ);
     235              43 :             memcpy(VARDATA(tuple_data_bytea), (char *) tuphdr + tuphdr->t_hoff,
     236                 :                    tuple_data_len);
     237              43 :             values[13] = PointerGetDatum(tuple_data_bytea);
     238                 : 
     239                 :             /*
     240                 :              * We already checked that the item is completely within the raw
     241                 :              * page passed to us, with the length given in the line pointer.
     242                 :              * Let's check that t_hoff doesn't point over lp_len, before using
     243                 :              * it to access t_bits and oid.
     244                 :              */
     245              43 :             if (tuphdr->t_hoff >= SizeofHeapTupleHeader &&
     246              43 :                 tuphdr->t_hoff <= lp_len &&
     247              43 :                 tuphdr->t_hoff == MAXALIGN(tuphdr->t_hoff))
     248                 :             {
     249              43 :                 if (tuphdr->t_infomask & HEAP_HASNULL)
     250                 :                 {
     251                 :                     int         bits_len;
     252                 : 
     253               2 :                     bits_len =
     254               2 :                         BITMAPLEN(HeapTupleHeaderGetNatts(tuphdr)) * BITS_PER_BYTE;
     255               2 :                     values[11] = CStringGetTextDatum(bits_to_text(tuphdr->t_bits, bits_len));
     256                 :                 }
     257                 :                 else
     258              41 :                     nulls[11] = true;
     259                 : 
     260              43 :                 if (tuphdr->t_infomask & HEAP_HASOID_OLD)
     261 UBC           0 :                     values[12] = HeapTupleHeaderGetOidOld(tuphdr);
     262                 :                 else
     263 CBC          43 :                     nulls[12] = true;
     264                 :             }
     265                 :             else
     266                 :             {
     267 UBC           0 :                 nulls[11] = true;
     268               0 :                 nulls[12] = true;
     269                 :             }
     270                 :         }
     271                 :         else
     272                 :         {
     273                 :             /*
     274                 :              * The line pointer is not used, or it's invalid. Set the rest of
     275                 :              * the fields to NULL
     276                 :              */
     277                 :             int         i;
     278                 : 
     279 CBC          55 :             for (i = 4; i <= 13; i++)
     280              50 :                 nulls[i] = true;
     281                 :         }
     282                 : 
     283                 :         /* Build and return the result tuple. */
     284              48 :         resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
     285              48 :         result = HeapTupleGetDatum(resultTuple);
     286                 : 
     287              48 :         inter_call_data->offset++;
     288                 : 
     289              48 :         SRF_RETURN_NEXT(fctx, result);
     290                 :     }
     291                 :     else
     292               5 :         SRF_RETURN_DONE(fctx);
     293                 : }
     294                 : 
     295                 : /*
     296                 :  * tuple_data_split_internal
     297                 :  *
     298                 :  * Split raw tuple data taken directly from a page into an array of bytea
     299                 :  * elements. This routine does a lookup on NULL values and creates array
     300                 :  * elements accordingly. This is a reimplementation of nocachegetattr()
     301                 :  * in heaptuple.c simplified for educational purposes.
     302                 :  */
     303                 : static Datum
     304               2 : tuple_data_split_internal(Oid relid, char *tupdata,
     305                 :                           uint16 tupdata_len, uint16 t_infomask,
     306                 :                           uint16 t_infomask2, bits8 *t_bits,
     307                 :                           bool do_detoast)
     308                 : {
     309                 :     ArrayBuildState *raw_attrs;
     310                 :     int         nattrs;
     311                 :     int         i;
     312               2 :     int         off = 0;
     313                 :     Relation    rel;
     314                 :     TupleDesc   tupdesc;
     315                 : 
     316                 :     /* Get tuple descriptor from relation OID */
     317               2 :     rel = relation_open(relid, AccessShareLock);
     318               2 :     tupdesc = RelationGetDescr(rel);
     319                 : 
     320               2 :     raw_attrs = initArrayResult(BYTEAOID, CurrentMemoryContext, false);
     321               2 :     nattrs = tupdesc->natts;
     322                 : 
     323               2 :     if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
     324 UBC           0 :         ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     325                 :                         errmsg("only heap AM is supported")));
     326                 : 
     327 CBC           2 :     if (nattrs < (t_infomask2 & HEAP_NATTS_MASK))
     328 UBC           0 :         ereport(ERROR,
     329                 :                 (errcode(ERRCODE_DATA_CORRUPTED),
     330                 :                  errmsg("number of attributes in tuple header is greater than number of attributes in tuple descriptor")));
     331                 : 
     332 CBC          12 :     for (i = 0; i < nattrs; i++)
     333                 :     {
     334                 :         Form_pg_attribute attr;
     335                 :         bool        is_null;
     336              10 :         bytea      *attr_data = NULL;
     337                 : 
     338              10 :         attr = TupleDescAttr(tupdesc, i);
     339                 : 
     340                 :         /*
     341                 :          * Tuple header can specify fewer attributes than tuple descriptor as
     342                 :          * ALTER TABLE ADD COLUMN without DEFAULT keyword does not actually
     343                 :          * change tuples in pages, so attributes with numbers greater than
     344                 :          * (t_infomask2 & HEAP_NATTS_MASK) should be treated as NULL.
     345                 :          */
     346              10 :         if (i >= (t_infomask2 & HEAP_NATTS_MASK))
     347 UBC           0 :             is_null = true;
     348                 :         else
     349 CBC          10 :             is_null = (t_infomask & HEAP_HASNULL) && att_isnull(i, t_bits);
     350                 : 
     351              10 :         if (!is_null)
     352                 :         {
     353                 :             int         len;
     354                 : 
     355               4 :             if (attr->attlen == -1)
     356                 :             {
     357 UBC           0 :                 off = att_align_pointer(off, attr->attalign, -1,
     358                 :                                         tupdata + off);
     359                 : 
     360                 :                 /*
     361                 :                  * As VARSIZE_ANY throws an exception if it can't properly
     362                 :                  * detect the type of external storage in macros VARTAG_SIZE,
     363                 :                  * this check is repeated to have a nicer error handling.
     364                 :                  */
     365               0 :                 if (VARATT_IS_EXTERNAL(tupdata + off) &&
     366               0 :                     !VARATT_IS_EXTERNAL_ONDISK(tupdata + off) &&
     367               0 :                     !VARATT_IS_EXTERNAL_INDIRECT(tupdata + off))
     368               0 :                     ereport(ERROR,
     369                 :                             (errcode(ERRCODE_DATA_CORRUPTED),
     370                 :                              errmsg("first byte of varlena attribute is incorrect for attribute %d", i)));
     371                 : 
     372               0 :                 len = VARSIZE_ANY(tupdata + off);
     373                 :             }
     374                 :             else
     375                 :             {
     376 CBC           4 :                 off = att_align_nominal(off, attr->attalign);
     377               4 :                 len = attr->attlen;
     378                 :             }
     379                 : 
     380               4 :             if (tupdata_len < off + len)
     381 UBC           0 :                 ereport(ERROR,
     382                 :                         (errcode(ERRCODE_DATA_CORRUPTED),
     383                 :                          errmsg("unexpected end of tuple data")));
     384                 : 
     385 CBC           4 :             if (attr->attlen == -1 && do_detoast)
     386 UNC           0 :                 attr_data = pg_detoast_datum_copy((struct varlena *) (tupdata + off));
     387                 :             else
     388                 :             {
     389 CBC           4 :                 attr_data = (bytea *) palloc(len + VARHDRSZ);
     390               4 :                 SET_VARSIZE(attr_data, len + VARHDRSZ);
     391               4 :                 memcpy(VARDATA(attr_data), tupdata + off, len);
     392                 :             }
     393                 : 
     394               4 :             off = att_addlength_pointer(off, attr->attlen,
     395                 :                                         tupdata + off);
     396                 :         }
     397                 : 
     398              10 :         raw_attrs = accumArrayResult(raw_attrs, PointerGetDatum(attr_data),
     399                 :                                      is_null, BYTEAOID, CurrentMemoryContext);
     400              10 :         if (attr_data)
     401               4 :             pfree(attr_data);
     402                 :     }
     403                 : 
     404               2 :     if (tupdata_len != off)
     405 UBC           0 :         ereport(ERROR,
     406                 :                 (errcode(ERRCODE_DATA_CORRUPTED),
     407                 :                  errmsg("end of tuple reached without looking at all its data")));
     408                 : 
     409 CBC           2 :     relation_close(rel, AccessShareLock);
     410                 : 
     411               2 :     return makeArrayResult(raw_attrs, CurrentMemoryContext);
     412                 : }
     413                 : 
     414                 : /*
     415                 :  * tuple_data_split
     416                 :  *
     417                 :  * Split raw tuple data taken directly from page into distinct elements
     418                 :  * taking into account null values.
     419                 :  */
     420              13 : PG_FUNCTION_INFO_V1(tuple_data_split);
     421                 : 
     422                 : Datum
     423               2 : tuple_data_split(PG_FUNCTION_ARGS)
     424                 : {
     425                 :     Oid         relid;
     426                 :     bytea      *raw_data;
     427                 :     uint16      t_infomask;
     428                 :     uint16      t_infomask2;
     429                 :     char       *t_bits_str;
     430               2 :     bool        do_detoast = false;
     431               2 :     bits8      *t_bits = NULL;
     432                 :     Datum       res;
     433                 : 
     434               2 :     relid = PG_GETARG_OID(0);
     435               2 :     raw_data = PG_ARGISNULL(1) ? NULL : PG_GETARG_BYTEA_P(1);
     436               2 :     t_infomask = PG_GETARG_INT16(2);
     437               2 :     t_infomask2 = PG_GETARG_INT16(3);
     438               2 :     t_bits_str = PG_ARGISNULL(4) ? NULL :
     439               1 :         text_to_cstring(PG_GETARG_TEXT_PP(4));
     440                 : 
     441               2 :     if (PG_NARGS() >= 6)
     442 UBC           0 :         do_detoast = PG_GETARG_BOOL(5);
     443                 : 
     444 CBC           2 :     if (!superuser())
     445 UBC           0 :         ereport(ERROR,
     446                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     447                 :                  errmsg("must be superuser to use raw page functions")));
     448                 : 
     449 CBC           2 :     if (!raw_data)
     450 UBC           0 :         PG_RETURN_NULL();
     451                 : 
     452                 :     /*
     453                 :      * Convert t_bits string back to the bits8 array as represented in the
     454                 :      * tuple header.
     455                 :      */
     456 CBC           2 :     if (t_infomask & HEAP_HASNULL)
     457                 :     {
     458                 :         size_t      bits_str_len;
     459                 :         size_t      bits_len;
     460                 : 
     461               1 :         bits_len = BITMAPLEN(t_infomask2 & HEAP_NATTS_MASK) * BITS_PER_BYTE;
     462               1 :         if (!t_bits_str)
     463 UBC           0 :             ereport(ERROR,
     464                 :                     (errcode(ERRCODE_DATA_CORRUPTED),
     465                 :                      errmsg("t_bits string must not be NULL")));
     466                 : 
     467 CBC           1 :         bits_str_len = strlen(t_bits_str);
     468               1 :         if (bits_len != bits_str_len)
     469 UBC           0 :             ereport(ERROR,
     470                 :                     (errcode(ERRCODE_DATA_CORRUPTED),
     471                 :                      errmsg("unexpected length of t_bits string: %zu, expected %zu",
     472                 :                             bits_str_len, bits_len)));
     473                 : 
     474                 :         /* do the conversion */
     475 CBC           1 :         t_bits = text_to_bits(t_bits_str, bits_str_len);
     476                 :     }
     477                 :     else
     478                 :     {
     479               1 :         if (t_bits_str)
     480 UBC           0 :             ereport(ERROR,
     481                 :                     (errcode(ERRCODE_DATA_CORRUPTED),
     482                 :                      errmsg("t_bits string is expected to be NULL, but instead it is %zu bytes long",
     483                 :                             strlen(t_bits_str))));
     484                 :     }
     485                 : 
     486                 :     /* Split tuple data */
     487 CBC           2 :     res = tuple_data_split_internal(relid, (char *) raw_data + VARHDRSZ,
     488               2 :                                     VARSIZE(raw_data) - VARHDRSZ,
     489                 :                                     t_infomask, t_infomask2, t_bits,
     490                 :                                     do_detoast);
     491                 : 
     492               2 :     if (t_bits)
     493               1 :         pfree(t_bits);
     494                 : 
     495 GNC           2 :     PG_RETURN_DATUM(res);
     496                 : }
     497                 : 
     498                 : /*
     499                 :  * heap_tuple_infomask_flags
     500                 :  *
     501                 :  * Decode into a human-readable format t_infomask and t_infomask2 associated
     502                 :  * to a tuple.  All the flags are described in access/htup_details.h.
     503                 :  */
     504 CBC           7 : PG_FUNCTION_INFO_V1(heap_tuple_infomask_flags);
     505                 : 
     506                 : Datum
     507               9 : heap_tuple_infomask_flags(PG_FUNCTION_ARGS)
     508                 : {
     509                 : #define HEAP_TUPLE_INFOMASK_COLS 2
     510 GNC           9 :     Datum       values[HEAP_TUPLE_INFOMASK_COLS] = {0};
     511               9 :     bool        nulls[HEAP_TUPLE_INFOMASK_COLS] = {0};
     512 CBC           9 :     uint16      t_infomask = PG_GETARG_INT16(0);
     513               9 :     uint16      t_infomask2 = PG_GETARG_INT16(1);
     514               9 :     int         cnt = 0;
     515                 :     ArrayType  *a;
     516                 :     int         bitcnt;
     517                 :     Datum      *flags;
     518                 :     TupleDesc   tupdesc;
     519                 :     HeapTuple   tuple;
     520                 : 
     521               9 :     if (!superuser())
     522 UBC           0 :         ereport(ERROR,
     523                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     524                 :                  errmsg("must be superuser to use raw page functions")));
     525                 : 
     526                 :     /* Build a tuple descriptor for our result type */
     527 CBC           9 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     528 UBC           0 :         elog(ERROR, "return type must be a row type");
     529                 : 
     530 CBC           9 :     bitcnt = pg_popcount((const char *) &t_infomask, sizeof(uint16)) +
     531               9 :         pg_popcount((const char *) &t_infomask2, sizeof(uint16));
     532                 : 
     533 ECB             :     /* If no flags, return a set of empty arrays */
     534 CBC           9 :     if (bitcnt <= 0)
     535 ECB             :     {
     536 GIC           1 :         values[0] = PointerGetDatum(construct_empty_array(TEXTOID));
     537               1 :         values[1] = PointerGetDatum(construct_empty_array(TEXTOID));
     538               1 :         tuple = heap_form_tuple(tupdesc, values, nulls);
     539 CBC           1 :         PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
     540                 :     }
     541                 : 
     542 ECB             :     /* build set of raw flags */
     543 CBC           8 :     flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
     544 ECB             : 
     545                 :     /* decode t_infomask */
     546 CBC           8 :     if ((t_infomask & HEAP_HASNULL) != 0)
     547               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_HASNULL");
     548               8 :     if ((t_infomask & HEAP_HASVARWIDTH) != 0)
     549               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_HASVARWIDTH");
     550               8 :     if ((t_infomask & HEAP_HASEXTERNAL) != 0)
     551               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_HASEXTERNAL");
     552               8 :     if ((t_infomask & HEAP_HASOID_OLD) != 0)
     553               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_HASOID_OLD");
     554               8 :     if ((t_infomask & HEAP_XMAX_KEYSHR_LOCK) != 0)
     555               4 :         flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_KEYSHR_LOCK");
     556               8 :     if ((t_infomask & HEAP_COMBOCID) != 0)
     557               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_COMBOCID");
     558               8 :     if ((t_infomask & HEAP_XMAX_EXCL_LOCK) != 0)
     559               3 :         flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_EXCL_LOCK");
     560               8 :     if ((t_infomask & HEAP_XMAX_LOCK_ONLY) != 0)
     561               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_LOCK_ONLY");
     562               8 :     if ((t_infomask & HEAP_XMIN_COMMITTED) != 0)
     563               4 :         flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_COMMITTED");
     564               8 :     if ((t_infomask & HEAP_XMIN_INVALID) != 0)
     565               4 :         flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_INVALID");
     566               8 :     if ((t_infomask & HEAP_XMAX_COMMITTED) != 0)
     567               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_COMMITTED");
     568               8 :     if ((t_infomask & HEAP_XMAX_INVALID) != 0)
     569               3 :         flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_INVALID");
     570               8 :     if ((t_infomask & HEAP_XMAX_IS_MULTI) != 0)
     571               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_IS_MULTI");
     572               8 :     if ((t_infomask & HEAP_UPDATED) != 0)
     573               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_UPDATED");
     574 GIC           8 :     if ((t_infomask & HEAP_MOVED_OFF) != 0)
     575               4 :         flags[cnt++] = CStringGetTextDatum("HEAP_MOVED_OFF");
     576 CBC           8 :     if ((t_infomask & HEAP_MOVED_IN) != 0)
     577               4 :         flags[cnt++] = CStringGetTextDatum("HEAP_MOVED_IN");
     578 ECB             : 
     579                 :     /* decode t_infomask2 */
     580 CBC           8 :     if ((t_infomask2 & HEAP_KEYS_UPDATED) != 0)
     581               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_KEYS_UPDATED");
     582 GIC           8 :     if ((t_infomask2 & HEAP_HOT_UPDATED) != 0)
     583               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_HOT_UPDATED");
     584 CBC           8 :     if ((t_infomask2 & HEAP_ONLY_TUPLE) != 0)
     585               2 :         flags[cnt++] = CStringGetTextDatum("HEAP_ONLY_TUPLE");
     586 ECB             : 
     587                 :     /* build value */
     588 GIC           8 :     Assert(cnt <= bitcnt);
     589 GNC           8 :     a = construct_array_builtin(flags, cnt, TEXTOID);
     590 GIC           8 :     values[0] = PointerGetDatum(a);
     591                 : 
     592 ECB             :     /*
     593                 :      * Build set of combined flags.  Use the same array as previously, this
     594                 :      * keeps the code simple.
     595                 :      */
     596 CBC           8 :     cnt = 0;
     597              85 :     MemSet(flags, 0, sizeof(Datum) * bitcnt);
     598 ECB             : 
     599                 :     /* decode combined masks of t_infomask */
     600 CBC           8 :     if ((t_infomask & HEAP_XMAX_SHR_LOCK) == HEAP_XMAX_SHR_LOCK)
     601               3 :         flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_SHR_LOCK");
     602 GIC           8 :     if ((t_infomask & HEAP_XMIN_FROZEN) == HEAP_XMIN_FROZEN)
     603               4 :         flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_FROZEN");
     604 CBC           8 :     if ((t_infomask & HEAP_MOVED) == HEAP_MOVED)
     605               4 :         flags[cnt++] = CStringGetTextDatum("HEAP_MOVED");
     606                 : 
     607 ECB             :     /* Build an empty array if there are no combined flags */
     608 CBC           8 :     if (cnt == 0)
     609               1 :         a = construct_empty_array(TEXTOID);
     610                 :     else
     611 GNC           7 :         a = construct_array_builtin(flags, cnt, TEXTOID);
     612 CBC           8 :     pfree(flags);
     613               8 :     values[1] = PointerGetDatum(a);
     614                 : 
     615                 :     /* Returns the record as Datum */
     616 GIC           8 :     tuple = heap_form_tuple(tupdesc, values, nulls);
     617               8 :     PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
     618                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a