LCOV - code coverage report
Current view: top level - contrib/pageinspect - rawpage.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 78 93 83.9 %
Date: 2019-11-15 23:07:02 Functions: 11 11 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-2019, 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          26 : 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          28 : PG_FUNCTION_INFO_V1(get_raw_page);
      44             : 
      45             : Datum
      46         122 : get_raw_page(PG_FUNCTION_ARGS)
      47             : {
      48         122 :     text       *relname = PG_GETARG_TEXT_PP(0);
      49         122 :     uint32      blkno = PG_GETARG_UINT32(1);
      50             :     bytea      *raw_page;
      51             : 
      52             :     /*
      53             :      * We don't normally bother to check the number of arguments to a C
      54             :      * function, but here it's needed for safety because early 8.4 beta
      55             :      * releases mistakenly redefined get_raw_page() as taking three arguments.
      56             :      */
      57         122 :     if (PG_NARGS() != 2)
      58           0 :         ereport(ERROR,
      59             :                 (errmsg("wrong number of arguments to get_raw_page()"),
      60             :                  errhint("Run the updated pageinspect.sql script.")));
      61             : 
      62         122 :     raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
      63             : 
      64         112 :     PG_RETURN_BYTEA_P(raw_page);
      65             : }
      66             : 
      67             : /*
      68             :  * get_raw_page_fork
      69             :  *
      70             :  * Same, for any fork
      71             :  */
      72           8 : PG_FUNCTION_INFO_V1(get_raw_page_fork);
      73             : 
      74             : Datum
      75          20 : get_raw_page_fork(PG_FUNCTION_ARGS)
      76             : {
      77          20 :     text       *relname = PG_GETARG_TEXT_PP(0);
      78          20 :     text       *forkname = PG_GETARG_TEXT_PP(1);
      79          20 :     uint32      blkno = PG_GETARG_UINT32(2);
      80             :     bytea      *raw_page;
      81             :     ForkNumber  forknum;
      82             : 
      83          20 :     forknum = forkname_to_number(text_to_cstring(forkname));
      84             : 
      85          18 :     raw_page = get_raw_page_internal(relname, forknum, blkno);
      86             : 
      87          12 :     PG_RETURN_BYTEA_P(raw_page);
      88             : }
      89             : 
      90             : /*
      91             :  * workhorse
      92             :  */
      93             : static bytea *
      94         140 : get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
      95             : {
      96             :     bytea      *raw_page;
      97             :     RangeVar   *relrv;
      98             :     Relation    rel;
      99             :     char       *raw_page_data;
     100             :     Buffer      buf;
     101             : 
     102         140 :     if (!superuser())
     103           0 :         ereport(ERROR,
     104             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     105             :                  (errmsg("must be superuser to use raw page functions"))));
     106             : 
     107         140 :     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
     108         140 :     rel = relation_openrv(relrv, AccessShareLock);
     109             : 
     110             :     /* Check that this relation has storage */
     111         138 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
     112           0 :         ereport(ERROR,
     113             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     114             :                  errmsg("cannot get raw page from view \"%s\"",
     115             :                         RelationGetRelationName(rel))));
     116         138 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
     117           0 :         ereport(ERROR,
     118             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     119             :                  errmsg("cannot get raw page from composite type \"%s\"",
     120             :                         RelationGetRelationName(rel))));
     121         138 :     if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
     122           0 :         ereport(ERROR,
     123             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     124             :                  errmsg("cannot get raw page from foreign table \"%s\"",
     125             :                         RelationGetRelationName(rel))));
     126         138 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
     127           2 :         ereport(ERROR,
     128             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     129             :                  errmsg("cannot get raw page from partitioned table \"%s\"",
     130             :                         RelationGetRelationName(rel))));
     131         136 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
     132           2 :         ereport(ERROR,
     133             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     134             :                  errmsg("cannot get raw page from partitioned index \"%s\"",
     135             :                         RelationGetRelationName(rel))));
     136             : 
     137             :     /*
     138             :      * Reject attempts to read non-local temporary relations; we would be
     139             :      * likely to get wrong data since we have no visibility into the owning
     140             :      * session's local buffers.
     141             :      */
     142         134 :     if (RELATION_IS_OTHER_TEMP(rel))
     143           0 :         ereport(ERROR,
     144             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     145             :                  errmsg("cannot access temporary tables of other sessions")));
     146             : 
     147         134 :     if (blkno >= RelationGetNumberOfBlocksInFork(rel, forknum))
     148          10 :         ereport(ERROR,
     149             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     150             :                  errmsg("block number %u is out of range for relation \"%s\"",
     151             :                         blkno, RelationGetRelationName(rel))));
     152             : 
     153             :     /* Initialize buffer to copy to */
     154         124 :     raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
     155         124 :     SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
     156         124 :     raw_page_data = VARDATA(raw_page);
     157             : 
     158             :     /* Take a verbatim copy of the page */
     159             : 
     160         124 :     buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
     161         124 :     LockBuffer(buf, BUFFER_LOCK_SHARE);
     162             : 
     163         124 :     memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
     164             : 
     165         124 :     LockBuffer(buf, BUFFER_LOCK_UNLOCK);
     166         124 :     ReleaseBuffer(buf);
     167             : 
     168         124 :     relation_close(rel, AccessShareLock);
     169             : 
     170         124 :     return raw_page;
     171             : }
     172             : 
     173             : 
     174             : /*
     175             :  * get_page_from_raw
     176             :  *
     177             :  * Get a palloc'd, maxalign'ed page image from the result of get_raw_page()
     178             :  *
     179             :  * On machines with MAXALIGN = 8, the payload of a bytea is not maxaligned,
     180             :  * since it will start 4 bytes into a palloc'd value.  On alignment-picky
     181             :  * machines, this will cause failures in accesses to 8-byte-wide values
     182             :  * within the page.  We don't need to worry if accessing only 4-byte or
     183             :  * smaller fields, but when examining a struct that contains 8-byte fields,
     184             :  * use this function for safety.
     185             :  */
     186             : Page
     187          58 : get_page_from_raw(bytea *raw_page)
     188             : {
     189             :     Page        page;
     190             :     int         raw_page_size;
     191             : 
     192          58 :     raw_page_size = VARSIZE_ANY_EXHDR(raw_page);
     193             : 
     194          58 :     if (raw_page_size != BLCKSZ)
     195           0 :         ereport(ERROR,
     196             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     197             :                  errmsg("invalid page size"),
     198             :                  errdetail("Expected %d bytes, got %d.",
     199             :                            BLCKSZ, raw_page_size)));
     200             : 
     201          58 :     page = palloc(raw_page_size);
     202             : 
     203          58 :     memcpy(page, VARDATA_ANY(raw_page), raw_page_size);
     204             : 
     205          58 :     return page;
     206             : }
     207             : 
     208             : 
     209             : /*
     210             :  * page_header
     211             :  *
     212             :  * Allows inspection of page header fields of a raw page
     213             :  */
     214             : 
     215           8 : PG_FUNCTION_INFO_V1(page_header);
     216             : 
     217             : Datum
     218           2 : page_header(PG_FUNCTION_ARGS)
     219             : {
     220           2 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     221             :     int         raw_page_size;
     222             : 
     223             :     TupleDesc   tupdesc;
     224             : 
     225             :     Datum       result;
     226             :     HeapTuple   tuple;
     227             :     Datum       values[9];
     228             :     bool        nulls[9];
     229             : 
     230             :     PageHeader  page;
     231             :     XLogRecPtr  lsn;
     232             : 
     233           2 :     if (!superuser())
     234           0 :         ereport(ERROR,
     235             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     236             :                  (errmsg("must be superuser to use raw page functions"))));
     237             : 
     238           2 :     raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
     239             : 
     240             :     /*
     241             :      * Check that enough data was supplied, so that we don't try to access
     242             :      * fields outside the supplied buffer.
     243             :      */
     244           2 :     if (raw_page_size < SizeOfPageHeaderData)
     245           0 :         ereport(ERROR,
     246             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     247             :                  errmsg("input page too small (%d bytes)", raw_page_size)));
     248             : 
     249           2 :     page = (PageHeader) VARDATA(raw_page);
     250             : 
     251             :     /* Build a tuple descriptor for our result type */
     252           2 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     253           0 :         elog(ERROR, "return type must be a row type");
     254             : 
     255             :     /* Extract information from the page header */
     256             : 
     257           2 :     lsn = PageGetLSN(page);
     258             : 
     259             :     /* pageinspect >= 1.2 uses pg_lsn instead of text for the LSN field. */
     260           2 :     if (TupleDescAttr(tupdesc, 0)->atttypid == TEXTOID)
     261             :     {
     262             :         char        lsnchar[64];
     263             : 
     264           0 :         snprintf(lsnchar, sizeof(lsnchar), "%X/%X",
     265           0 :                  (uint32) (lsn >> 32), (uint32) lsn);
     266           0 :         values[0] = CStringGetTextDatum(lsnchar);
     267             :     }
     268             :     else
     269           2 :         values[0] = LSNGetDatum(lsn);
     270           2 :     values[1] = UInt16GetDatum(page->pd_checksum);
     271           2 :     values[2] = UInt16GetDatum(page->pd_flags);
     272           2 :     values[3] = UInt16GetDatum(page->pd_lower);
     273           2 :     values[4] = UInt16GetDatum(page->pd_upper);
     274           2 :     values[5] = UInt16GetDatum(page->pd_special);
     275           2 :     values[6] = UInt16GetDatum(PageGetPageSize(page));
     276           2 :     values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page));
     277           2 :     values[8] = TransactionIdGetDatum(page->pd_prune_xid);
     278             : 
     279             :     /* Build and return the tuple. */
     280             : 
     281           2 :     memset(nulls, 0, sizeof(nulls));
     282             : 
     283           2 :     tuple = heap_form_tuple(tupdesc, values, nulls);
     284           2 :     result = HeapTupleGetDatum(tuple);
     285             : 
     286           2 :     PG_RETURN_DATUM(result);
     287             : }
     288             : 
     289             : /*
     290             :  * page_checksum
     291             :  *
     292             :  * Compute checksum of a raw page
     293             :  */
     294             : 
     295           8 : PG_FUNCTION_INFO_V1(page_checksum);
     296             : 
     297             : Datum
     298           2 : page_checksum(PG_FUNCTION_ARGS)
     299             : {
     300           2 :     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
     301           2 :     uint32      blkno = PG_GETARG_INT32(1);
     302             :     int         raw_page_size;
     303             :     PageHeader  page;
     304             : 
     305           2 :     if (!superuser())
     306           0 :         ereport(ERROR,
     307             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     308             :                  (errmsg("must be superuser to use raw page functions"))));
     309             : 
     310           2 :     raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
     311             : 
     312             :     /*
     313             :      * Check that the supplied page is of the right size.
     314             :      */
     315           2 :     if (raw_page_size != BLCKSZ)
     316           0 :         ereport(ERROR,
     317             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     318             :                  errmsg("incorrect size of input page (%d bytes)", raw_page_size)));
     319             : 
     320           2 :     page = (PageHeader) VARDATA(raw_page);
     321             : 
     322           2 :     PG_RETURN_INT16(pg_checksum_page((char *) page, blkno));
     323             : }

Generated by: LCOV version 1.13