/*-------------------------------------------------------------------------
*
* rawpage . c
* Functions to extract a raw page as bytea and inspect it
*
* Access - method specific inspection functions are in separate files .
*
* Copyright ( c ) 2007 - 2025 , PostgreSQL Global Development Group
*
* IDENTIFICATION
* contrib / pageinspect / rawpage . c
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "pageinspect.h"
#include "storage/bufmgr.h"
#include "storage/checksum.h"
#include "utils/builtins.h"
#include "utils/pg_lsn.h"
#include "utils/rel.h"
#include "utils/varlena.h"
PG_MODULE_MAGIC_EXT(
.name = "pageinspect" ,
.version = PG_VERSION
);
static bytea *get_raw_page_internal(text *relname, ForkNumber forknum,
BlockNumber blkno);
/*
* get_raw_page
*
* Returns a copy of a page from shared buffers as a bytea
*/
PG_FUNCTION_INFO_V1(get_raw_page_1_9);
Datum
get_raw_page_1_9(PG_FUNCTION_ARGS)
{
text *relname = PG_GETARG_TEXT_PP(0 );
int64 blkno = PG_GETARG_INT64(1 );
bytea *raw_page;
if (blkno < 0 || blkno > MaxBlockNumber)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid block number" )));
raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
PG_RETURN_BYTEA_P(raw_page);
}
/*
* entry point for old extension version
*/
PG_FUNCTION_INFO_V1(get_raw_page);
Datum
get_raw_page(PG_FUNCTION_ARGS)
{
text *relname = PG_GETARG_TEXT_PP(0 );
uint32 blkno = PG_GETARG_UINT32(1 );
bytea *raw_page;
/*
* We don ' t normally bother to check the number of arguments to a C
* function , but here it ' s needed for safety because early 8 . 4 beta
* releases mistakenly redefined get_raw_page ( ) as taking three arguments .
*/
if (PG_NARGS() != 2 )
ereport(ERROR,
(errmsg("wrong number of arguments to get_raw_page()" ),
errhint("Run the updated pageinspect.sql script." )));
raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
PG_RETURN_BYTEA_P(raw_page);
}
/*
* get_raw_page_fork
*
* Same , for any fork
*/
PG_FUNCTION_INFO_V1(get_raw_page_fork_1_9);
Datum
get_raw_page_fork_1_9(PG_FUNCTION_ARGS)
{
text *relname = PG_GETARG_TEXT_PP(0 );
text *forkname = PG_GETARG_TEXT_PP(1 );
int64 blkno = PG_GETARG_INT64(2 );
bytea *raw_page;
ForkNumber forknum;
forknum = forkname_to_number(text_to_cstring(forkname));
if (blkno < 0 || blkno > MaxBlockNumber)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid block number" )));
raw_page = get_raw_page_internal(relname, forknum, blkno);
PG_RETURN_BYTEA_P(raw_page);
}
/*
* Entry point for old extension version
*/
PG_FUNCTION_INFO_V1(get_raw_page_fork);
Datum
get_raw_page_fork(PG_FUNCTION_ARGS)
{
text *relname = PG_GETARG_TEXT_PP(0 );
text *forkname = PG_GETARG_TEXT_PP(1 );
uint32 blkno = PG_GETARG_UINT32(2 );
bytea *raw_page;
ForkNumber forknum;
forknum = forkname_to_number(text_to_cstring(forkname));
raw_page = get_raw_page_internal(relname, forknum, blkno);
PG_RETURN_BYTEA_P(raw_page);
}
/*
* workhorse
*/
static bytea *
get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
{
bytea *raw_page;
RangeVar *relrv;
Relation rel;
char *raw_page_data;
Buffer buf;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions" )));
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
rel = relation_openrv(relrv, AccessShareLock);
if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot get raw page from relation \" %s\"" ,
RelationGetRelationName(rel)),
errdetail_relkind_not_supported(rel->rd_rel->relkind)));
/*
* Reject attempts to read non - local temporary relations ; we would be
* likely to get wrong data since we have no visibility into the owning
* session ' s local buffers .
*/
if (RELATION_IS_OTHER_TEMP(rel))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot access temporary tables of other sessions" )));
if (blkno >= RelationGetNumberOfBlocksInFork(rel, forknum))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("block number %u is out of range for relation \" %s\"" ,
blkno, RelationGetRelationName(rel))));
/* Initialize buffer to copy to */
raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
raw_page_data = VARDATA(raw_page);
/* Take a verbatim copy of the page */
buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
LockBuffer(buf, BUFFER_LOCK_SHARE);
memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
ReleaseBuffer(buf);
relation_close(rel, AccessShareLock);
return raw_page;
}
/*
* get_page_from_raw
*
* Get a palloc ' d , maxalign ' ed page image from the result of get_raw_page ( )
*
* On machines with MAXALIGN = 8 , the payload of a bytea is not maxaligned ,
* since it will start 4 bytes into a palloc ' d value . On alignment - picky
* machines , this will cause failures in accesses to 8 - byte - wide values
* within the page . We don ' t need to worry if accessing only 4 - byte or
* smaller fields , but when examining a struct that contains 8 - byte fields ,
* use this function for safety .
*/
Page
get_page_from_raw(bytea *raw_page)
{
Page page;
int raw_page_size;
raw_page_size = VARSIZE_ANY_EXHDR(raw_page);
if (raw_page_size != BLCKSZ)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid page size" ),
errdetail("Expected %d bytes, got %d." ,
BLCKSZ, raw_page_size)));
page = palloc(raw_page_size);
memcpy(page, VARDATA_ANY(raw_page), raw_page_size);
return page;
}
/*
* page_header
*
* Allows inspection of page header fields of a raw page
*/
PG_FUNCTION_INFO_V1(page_header);
Datum
page_header(PG_FUNCTION_ARGS)
{
bytea *raw_page = PG_GETARG_BYTEA_P(0 );
TupleDesc tupdesc;
Datum result;
HeapTuple tuple;
Datum values[9 ];
bool nulls[9 ];
Page page;
PageHeader pageheader;
XLogRecPtr lsn;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions" )));
page = get_page_from_raw(raw_page);
pageheader = (PageHeader) page;
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type" );
/* Extract information from the page header */
lsn = PageGetLSN(page);
/* pageinspect >= 1.2 uses pg_lsn instead of text for the LSN field. */
if (TupleDescAttr(tupdesc, 0 )->atttypid == TEXTOID)
{
char lsnchar[64 ];
snprintf(lsnchar, sizeof (lsnchar), "%X/%X" , LSN_FORMAT_ARGS(lsn));
values[0 ] = CStringGetTextDatum(lsnchar);
}
else
values[0 ] = LSNGetDatum(lsn);
values[1 ] = UInt16GetDatum(pageheader->pd_checksum);
values[2 ] = UInt16GetDatum(pageheader->pd_flags);
/* pageinspect >= 1.10 uses int4 instead of int2 for those fields */
switch (TupleDescAttr(tupdesc, 3 )->atttypid)
{
case INT2OID:
Assert(TupleDescAttr(tupdesc, 4 )->atttypid == INT2OID &&
TupleDescAttr(tupdesc, 5 )->atttypid == INT2OID &&
TupleDescAttr(tupdesc, 6 )->atttypid == INT2OID);
values[3 ] = UInt16GetDatum(pageheader->pd_lower);
values[4 ] = UInt16GetDatum(pageheader->pd_upper);
values[5 ] = UInt16GetDatum(pageheader->pd_special);
values[6 ] = UInt16GetDatum(PageGetPageSize(page));
break ;
case INT4OID:
Assert(TupleDescAttr(tupdesc, 4 )->atttypid == INT4OID &&
TupleDescAttr(tupdesc, 5 )->atttypid == INT4OID &&
TupleDescAttr(tupdesc, 6 )->atttypid == INT4OID);
values[3 ] = Int32GetDatum(pageheader->pd_lower);
values[4 ] = Int32GetDatum(pageheader->pd_upper);
values[5 ] = Int32GetDatum(pageheader->pd_special);
values[6 ] = Int32GetDatum(PageGetPageSize(page));
break ;
default :
elog(ERROR, "incorrect output types" );
break ;
}
values[7 ] = UInt16GetDatum(PageGetPageLayoutVersion(page));
values[8 ] = TransactionIdGetDatum(pageheader->pd_prune_xid);
/* Build and return the tuple. */
memset(nulls, 0 , sizeof (nulls));
tuple = heap_form_tuple(tupdesc, values, nulls);
result = HeapTupleGetDatum(tuple);
PG_RETURN_DATUM(result);
}
/*
* page_checksum
*
* Compute checksum of a raw page
*/
PG_FUNCTION_INFO_V1(page_checksum_1_9);
PG_FUNCTION_INFO_V1(page_checksum);
static Datum
page_checksum_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
{
bytea *raw_page = PG_GETARG_BYTEA_P(0 );
int64 blkno = (ext_version == PAGEINSPECT_V1_8 ? PG_GETARG_UINT32(1 ) : PG_GETARG_INT64(1 ));
Page page;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions" )));
if (blkno < 0 || blkno > MaxBlockNumber)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid block number" )));
page = get_page_from_raw(raw_page);
if (PageIsNew(page))
PG_RETURN_NULL();
PG_RETURN_INT16(pg_checksum_page(page, blkno));
}
Datum
page_checksum_1_9(PG_FUNCTION_ARGS)
{
return page_checksum_internal(fcinfo, PAGEINSPECT_V1_9);
}
/*
* Entry point for old extension version
*/
Datum
page_checksum(PG_FUNCTION_ARGS)
{
return page_checksum_internal(fcinfo, PAGEINSPECT_V1_8);
}
Messung V0.5 in Prozent C=93 H=89 G=90
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland