LCOV - code coverage report
Current view: top level - contrib/pageinspect - rawpage.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 107 117 91.5 %
Date: 2024-03-19 09:11:38 Functions: 18 18 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * rawpage.c
       4             :  *    Functions to extract a raw page as bytea and inspect it
       5             :  *
       6             :  * Access-method specific inspection functions are in separate files.
       7             :  *
       8             :  * Copyright (c) 2007-2024, PostgreSQL Global Development Group
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    contrib/pageinspect/rawpage.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres.h"
      17             : 
      18             : #include "access/htup_details.h"
      19             : #include "access/relation.h"
      20             : #include "catalog/namespace.h"
      21             : #include "catalog/pg_type.h"
      22             : #include "funcapi.h"
      23             : #include "miscadmin.h"
      24             : #include "pageinspect.h"
      25             : #include "storage/bufmgr.h"
      26             : #include "storage/checksum.h"
      27             : #include "utils/builtins.h"
      28             : #include "utils/pg_lsn.h"
      29             : #include "utils/rel.h"
      30             : #include "utils/varlena.h"
      31             : 
      32          40 : PG_MODULE_MAGIC;
      33             : 
      34             : static bytea *get_raw_page_internal(text *relname, ForkNumber forknum,
      35             :                                     BlockNumber blkno);
      36             : 
      37             : 
      38             : /*
      39             :  * get_raw_page
      40             :  *
      41             :  * Returns a copy of a page from shared buffers as a bytea
      42             :  */
      43          40 : PG_FUNCTION_INFO_V1(get_raw_page_1_9);
      44             : 
      45             : Datum
      46         208 : get_raw_page_1_9(PG_FUNCTION_ARGS)
      47             : {
      48         208 :     text       *relname = PG_GETARG_TEXT_PP(0);
      49         208 :     int64       blkno = PG_GETARG_INT64(1);
      50             :     bytea      *raw_page;
      51             : 
      52         208 :     if (blkno < 0 || blkno > MaxBlockNumber)
      53           2 :         ereport(ERROR,
      54             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      55             :                  errmsg("invalid block number")));
      56             : 
      57         206 :     raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
      58             : 
      59         196 :     PG_RETURN_BYTEA_P(raw_page);
      60             : }
      61             : 
      62             : /*
      63             :  * entry point for old extension version
      64             :  */
      65          14 : PG_FUNCTION_INFO_V1(get_raw_page);
      66             : 
      67             : Datum
      68           4 : get_raw_page(PG_FUNCTION_ARGS)
      69             : {
      70           4 :     text       *relname = PG_GETARG_TEXT_PP(0);
      71           4 :     uint32      blkno = PG_GETARG_UINT32(1);
      72             :     bytea      *raw_page;
      73             : 
      74             :     /*
      75             :      * We don't normally bother to check the number of arguments to a C
      76             :      * function, but here it's needed for safety because early 8.4 beta
      77             :      * releases mistakenly redefined get_raw_page() as taking three arguments.
      78             :      */
      79           4 :     if (PG_NARGS() != 2)
      80           0 :         ereport(ERROR,
      81             :                 (errmsg("wrong number of arguments to get_raw_page()"),
      82             :                  errhint("Run the updated pageinspect.sql script.")));
      83             : 
      84           4 :     raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
      85             : 
      86           4 :     PG_RETURN_BYTEA_P(raw_page);
      87             : }
      88             : 
      89             : /*
      90             :  * get_raw_page_fork
      91             :  *
      92             :  * Same, for any fork
      93             :  */
      94          16 : PG_FUNCTION_INFO_V1(get_raw_page_fork_1_9);
      95             : 
      96             : Datum
      97          24 : get_raw_page_fork_1_9(PG_FUNCTION_ARGS)
      98             : {
      99          24 :     text       *relname = PG_GETARG_TEXT_PP(0);
     100          24 :     text       *forkname = PG_GETARG_TEXT_PP(1);
     101          24 :     int64       blkno = PG_GETARG_INT64(2);
     102             :     bytea      *raw_page;
     103             :     ForkNumber  forknum;
     104             : 
     105          24 :     forknum = forkname_to_number(text_to_cstring(forkname));
     106             : 
     107          22 :     if (blkno < 0 || blkno > MaxBlockNumber)
     108           2 :         ereport(ERROR,
     109             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     110             :                  errmsg("invalid block number")));
     111             : 
     112          20 :     raw_page = get_raw_page_internal(relname, forknum, blkno);
     113             : 
     114          14 :     PG_RETURN_BYTEA_P(raw_page);
     115             : }
     116             : 
     117             : /*
     118             :  * Entry point for old extension version
     119             :  */
     120          14 : PG_FUNCTION_INFO_V1(get_raw_page_fork);
     121             : 
     122             : Datum
     123           2 : get_raw_page_fork(PG_FUNCTION_ARGS)
     124             : {
     125           2 :     text       *relname = PG_GETARG_TEXT_PP(0);
     126           2 :     text       *forkname = PG_GETARG_TEXT_PP(1);
     127           2 :     uint32      blkno = PG_GETARG_UINT32(2);
     128             :     bytea      *raw_page;
     129             :     ForkNumber  forknum;
     130             : 
     131           2 :     forknum = forkname_to_number(text_to_cstring(forkname));
     132             : 
     133           2 :     raw_page = get_raw_page_internal(relname, forknum, blkno);
     134             : 
     135           2 :     PG_RETURN_BYTEA_P(raw_page);
     136             : }
     137             : 
     138             : /*
     139             :  * workhorse
     140             :  */
     141             : static bytea *
     142         232 : get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
     143             : {
     144             :     bytea      *raw_page;
     145             :     RangeVar   *relrv;
     146             :     Relation    rel;
     147             :     char       *raw_page_data;
     148             :     Buffer      buf;
     149             : 
     150         232 :     if (!superuser())
     151           0 :         ereport(ERROR,
     152             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     153             :                  errmsg("must be superuser to use raw page functions")));
     154             : 
     155         232 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     156         232 :     rel = relation_openrv(relrv, AccessShareLock);
     157             : 
     158         230 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
     159           4 :         ereport(ERROR,
     160             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     161             :                  errmsg("cannot get raw page from relation \"%s\"",
     162             :                         RelationGetRelationName(rel)),
     163             :                  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
     164             : 
     165             :     /*
     166             :      * Reject attempts to read non-local temporary relations; we would be
     167             :      * likely to get wrong data since we have no visibility into the owning
     168             :      * session's local buffers.
     169             :      */
     170         226 :     if (RELATION_IS_OTHER_TEMP(rel))
     171           0 :         ereport(ERROR,
     172             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     173             :                  errmsg("cannot access temporary tables of other sessions")));
     174             : 
     175         226 :     if (blkno >= RelationGetNumberOfBlocksInFork(rel, forknum))
     176          10 :         ereport(ERROR,
     177             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     178             :                  errmsg("block number %u is out of range for relation \"%s\"",
     179             :                         blkno, RelationGetRelationName(rel))));
     180             : 
     181             :     /* Initialize buffer to copy to */
     182         216 :     raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
     183         216 :     SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
     184         216 :     raw_page_data = VARDATA(raw_page);
     185             : 
     186             :     /* Take a verbatim copy of the page */
     187             : 
     188         216 :     buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
     189         216 :     LockBuffer(buf, BUFFER_LOCK_SHARE);
     190             : 
     191         216 :     memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
     192             : 
     193         216 :     LockBuffer(buf, BUFFER_LOCK_UNLOCK);
     194         216 :     ReleaseBuffer(buf);
     195             : 
     196         216 :     relation_close(rel, AccessShareLock);
     197             : 
     198         216 :     return raw_page;
     199             : }
     200             : 
     201             : 
     202             : /*
     203             :  * get_page_from_raw
     204             :  *
     205             :  * Get a palloc'd, maxalign'ed page image from the result of get_raw_page()
     206             :  *
     207             :  * On machines with MAXALIGN = 8, the payload of a bytea is not maxaligned,
     208             :  * since it will start 4 bytes into a palloc'd value.  On alignment-picky
     209             :  * machines, this will cause failures in accesses to 8-byte-wide values
     210             :  * within the page.  We don't need to worry if accessing only 4-byte or
     211             :  * smaller fields, but when examining a struct that contains 8-byte fields,
     212             :  * use this function for safety.
     213             :  */
     214             : Page
     215         284 : get_page_from_raw(bytea *raw_page)
     216             : {
     217             :     Page        page;
     218             :     int         raw_page_size;
     219             : 
     220         284 :     raw_page_size = VARSIZE_ANY_EXHDR(raw_page);
     221             : 
     222         284 :     if (raw_page_size != BLCKSZ)
     223          28 :         ereport(ERROR,
     224             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     225             :                  errmsg("invalid page size"),
     226             :                  errdetail("Expected %d bytes, got %d.",
     227             :                            BLCKSZ, raw_page_size)));
     228             : 
     229         256 :     page = palloc(raw_page_size);
     230             : 
     231         256 :     memcpy(page, VARDATA_ANY(raw_page), raw_page_size);
     232             : 
     233         256 :     return page;
     234             : }
     235             : 
     236             : 
     237             : /*
     238             :  * page_header
     239             :  *
     240             :  * Allows inspection of page header fields of a raw page
     241             :  */
     242             : 
     243          26 : PG_FUNCTION_INFO_V1(page_header);
     244             : 
     245             : Datum
     246           8 : page_header(PG_FUNCTION_ARGS)
     247             : {
     248           8 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     249             : 
     250             :     TupleDesc   tupdesc;
     251             : 
     252             :     Datum       result;
     253             :     HeapTuple   tuple;
     254             :     Datum       values[9];
     255             :     bool        nulls[9];
     256             : 
     257             :     Page        page;
     258             :     PageHeader  pageheader;
     259             :     XLogRecPtr  lsn;
     260             : 
     261           8 :     if (!superuser())
     262           0 :         ereport(ERROR,
     263             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     264             :                  errmsg("must be superuser to use raw page functions")));
     265             : 
     266           8 :     page = get_page_from_raw(raw_page);
     267           6 :     pageheader = (PageHeader) page;
     268             : 
     269             :     /* Build a tuple descriptor for our result type */
     270           6 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     271           0 :         elog(ERROR, "return type must be a row type");
     272             : 
     273             :     /* Extract information from the page header */
     274             : 
     275           6 :     lsn = PageGetLSN(page);
     276             : 
     277             :     /* pageinspect >= 1.2 uses pg_lsn instead of text for the LSN field. */
     278           6 :     if (TupleDescAttr(tupdesc, 0)->atttypid == TEXTOID)
     279             :     {
     280             :         char        lsnchar[64];
     281             : 
     282           0 :         snprintf(lsnchar, sizeof(lsnchar), "%X/%X", LSN_FORMAT_ARGS(lsn));
     283           0 :         values[0] = CStringGetTextDatum(lsnchar);
     284             :     }
     285             :     else
     286           6 :         values[0] = LSNGetDatum(lsn);
     287           6 :     values[1] = UInt16GetDatum(pageheader->pd_checksum);
     288           6 :     values[2] = UInt16GetDatum(pageheader->pd_flags);
     289             : 
     290             :     /* pageinspect >= 1.10 uses int4 instead of int2 for those fields */
     291           6 :     switch (TupleDescAttr(tupdesc, 3)->atttypid)
     292             :     {
     293           2 :         case INT2OID:
     294             :             Assert(TupleDescAttr(tupdesc, 4)->atttypid == INT2OID &&
     295             :                    TupleDescAttr(tupdesc, 5)->atttypid == INT2OID &&
     296             :                    TupleDescAttr(tupdesc, 6)->atttypid == INT2OID);
     297           2 :             values[3] = UInt16GetDatum(pageheader->pd_lower);
     298           2 :             values[4] = UInt16GetDatum(pageheader->pd_upper);
     299           2 :             values[5] = UInt16GetDatum(pageheader->pd_special);
     300           2 :             values[6] = UInt16GetDatum(PageGetPageSize(page));
     301           2 :             break;
     302           4 :         case INT4OID:
     303             :             Assert(TupleDescAttr(tupdesc, 4)->atttypid == INT4OID &&
     304             :                    TupleDescAttr(tupdesc, 5)->atttypid == INT4OID &&
     305             :                    TupleDescAttr(tupdesc, 6)->atttypid == INT4OID);
     306           4 :             values[3] = Int32GetDatum(pageheader->pd_lower);
     307           4 :             values[4] = Int32GetDatum(pageheader->pd_upper);
     308           4 :             values[5] = Int32GetDatum(pageheader->pd_special);
     309           4 :             values[6] = Int32GetDatum(PageGetPageSize(page));
     310           4 :             break;
     311           0 :         default:
     312           0 :             elog(ERROR, "incorrect output types");
     313             :             break;
     314             :     }
     315             : 
     316           6 :     values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page));
     317           6 :     values[8] = TransactionIdGetDatum(pageheader->pd_prune_xid);
     318             : 
     319             :     /* Build and return the tuple. */
     320             : 
     321           6 :     memset(nulls, 0, sizeof(nulls));
     322             : 
     323           6 :     tuple = heap_form_tuple(tupdesc, values, nulls);
     324           6 :     result = HeapTupleGetDatum(tuple);
     325             : 
     326           6 :     PG_RETURN_DATUM(result);
     327             : }
     328             : 
     329             : /*
     330             :  * page_checksum
     331             :  *
     332             :  * Compute checksum of a raw page
     333             :  */
     334             : 
     335          16 : PG_FUNCTION_INFO_V1(page_checksum_1_9);
     336          14 : PG_FUNCTION_INFO_V1(page_checksum);
     337             : 
     338             : static Datum
     339          46 : page_checksum_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
     340             : {
     341          46 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     342          46 :     int64       blkno = (ext_version == PAGEINSPECT_V1_8 ? PG_GETARG_UINT32(1) : PG_GETARG_INT64(1));
     343             :     Page        page;
     344             : 
     345          46 :     if (!superuser())
     346           0 :         ereport(ERROR,
     347             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     348             :                  errmsg("must be superuser to use raw page functions")));
     349             : 
     350          46 :     if (blkno < 0 || blkno > MaxBlockNumber)
     351           2 :         ereport(ERROR,
     352             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     353             :                  errmsg("invalid block number")));
     354             : 
     355          44 :     page = get_page_from_raw(raw_page);
     356             : 
     357          42 :     if (PageIsNew(page))
     358           2 :         PG_RETURN_NULL();
     359             : 
     360          40 :     PG_RETURN_INT16(pg_checksum_page((char *) page, blkno));
     361             : }
     362             : 
     363             : Datum
     364          44 : page_checksum_1_9(PG_FUNCTION_ARGS)
     365             : {
     366          44 :     return page_checksum_internal(fcinfo, PAGEINSPECT_V1_9);
     367             : }
     368             : 
     369             : /*
     370             :  * Entry point for old extension version
     371             :  */
     372             : Datum
     373           2 : page_checksum(PG_FUNCTION_ARGS)
     374             : {
     375           2 :     return page_checksum_internal(fcinfo, PAGEINSPECT_V1_8);
     376             : }

Generated by: LCOV version 1.14