PostgreSQL Source Code  git master
rawpage.c
Go to the documentation of this file.
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-2021, 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 
33 
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  */
44 
45 Datum
47 {
49  int64 blkno = PG_GETARG_INT64(1);
50  bytea *raw_page;
51 
52  if (blkno < 0 || blkno > MaxBlockNumber)
53  ereport(ERROR,
54  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
55  errmsg("invalid block number")));
56 
57  raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
58 
59  PG_RETURN_BYTEA_P(raw_page);
60 }
61 
62 /*
63  * entry point for old extension version
64  */
66 
67 Datum
69 {
71  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  if (PG_NARGS() != 2)
80  ereport(ERROR,
81  (errmsg("wrong number of arguments to get_raw_page()"),
82  errhint("Run the updated pageinspect.sql script.")));
83 
84  raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
85 
86  PG_RETURN_BYTEA_P(raw_page);
87 }
88 
89 /*
90  * get_raw_page_fork
91  *
92  * Same, for any fork
93  */
95 
96 Datum
98 {
100  text *forkname = PG_GETARG_TEXT_PP(1);
101  int64 blkno = PG_GETARG_INT64(2);
102  bytea *raw_page;
103  ForkNumber forknum;
104 
105  forknum = forkname_to_number(text_to_cstring(forkname));
106 
107  if (blkno < 0 || blkno > MaxBlockNumber)
108  ereport(ERROR,
109  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
110  errmsg("invalid block number")));
111 
112  raw_page = get_raw_page_internal(relname, forknum, blkno);
113 
114  PG_RETURN_BYTEA_P(raw_page);
115 }
116 
117 /*
118  * Entry point for old extension version
119  */
121 
122 Datum
124 {
126  text *forkname = PG_GETARG_TEXT_PP(1);
127  uint32 blkno = PG_GETARG_UINT32(2);
128  bytea *raw_page;
129  ForkNumber forknum;
130 
131  forknum = forkname_to_number(text_to_cstring(forkname));
132 
133  raw_page = get_raw_page_internal(relname, forknum, blkno);
134 
135  PG_RETURN_BYTEA_P(raw_page);
136 }
137 
138 /*
139  * workhorse
140  */
141 static bytea *
143 {
144  bytea *raw_page;
145  RangeVar *relrv;
146  Relation rel;
147  char *raw_page_data;
148  Buffer buf;
149 
150  if (!superuser())
151  ereport(ERROR,
152  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
153  errmsg("must be superuser to use raw page functions")));
154 
156  rel = relation_openrv(relrv, AccessShareLock);
157 
158  if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
159  ereport(ERROR,
160  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
161  errmsg("cannot get raw page from relation \"%s\"",
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  if (RELATION_IS_OTHER_TEMP(rel))
171  ereport(ERROR,
172  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
173  errmsg("cannot access temporary tables of other sessions")));
174 
175  if (blkno >= RelationGetNumberOfBlocksInFork(rel, forknum))
176  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  raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
183  SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
184  raw_page_data = VARDATA(raw_page);
185 
186  /* Take a verbatim copy of the page */
187 
188  buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
190 
191  memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
192 
194  ReleaseBuffer(buf);
195 
197 
198  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
216 {
217  Page page;
218  int raw_page_size;
219 
220  raw_page_size = VARSIZE_ANY_EXHDR(raw_page);
221 
222  if (raw_page_size != BLCKSZ)
223  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  page = palloc(raw_page_size);
230 
231  memcpy(page, VARDATA_ANY(raw_page), raw_page_size);
232 
233  return page;
234 }
235 
236 
237 /*
238  * page_header
239  *
240  * Allows inspection of page header fields of a raw page
241  */
242 
244 
245 Datum
247 {
248  bytea *raw_page = PG_GETARG_BYTEA_P(0);
249  int raw_page_size;
250 
251  TupleDesc tupdesc;
252 
253  Datum result;
254  HeapTuple tuple;
255  Datum values[9];
256  bool nulls[9];
257 
258  PageHeader page;
259  XLogRecPtr lsn;
260 
261  if (!superuser())
262  ereport(ERROR,
263  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
264  errmsg("must be superuser to use raw page functions")));
265 
266  raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
267 
268  /*
269  * Check that enough data was supplied, so that we don't try to access
270  * fields outside the supplied buffer.
271  */
272  if (raw_page_size < SizeOfPageHeaderData)
273  ereport(ERROR,
274  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
275  errmsg("input page too small (%d bytes)", raw_page_size)));
276 
277  page = (PageHeader) VARDATA(raw_page);
278 
279  /* Build a tuple descriptor for our result type */
280  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
281  elog(ERROR, "return type must be a row type");
282 
283  /* Extract information from the page header */
284 
285  lsn = PageGetLSN(page);
286 
287  /* pageinspect >= 1.2 uses pg_lsn instead of text for the LSN field. */
288  if (TupleDescAttr(tupdesc, 0)->atttypid == TEXTOID)
289  {
290  char lsnchar[64];
291 
292  snprintf(lsnchar, sizeof(lsnchar), "%X/%X", LSN_FORMAT_ARGS(lsn));
293  values[0] = CStringGetTextDatum(lsnchar);
294  }
295  else
296  values[0] = LSNGetDatum(lsn);
297  values[1] = UInt16GetDatum(page->pd_checksum);
298  values[2] = UInt16GetDatum(page->pd_flags);
299 
300  /* pageinspect >= 1.10 uses int4 instead of int2 for those fields */
301  switch (TupleDescAttr(tupdesc, 3)->atttypid)
302  {
303  case INT2OID:
304  Assert(TupleDescAttr(tupdesc, 4)->atttypid == INT2OID &&
305  TupleDescAttr(tupdesc, 5)->atttypid == INT2OID &&
306  TupleDescAttr(tupdesc, 6)->atttypid == INT2OID);
307  values[3] = UInt16GetDatum(page->pd_lower);
308  values[4] = UInt16GetDatum(page->pd_upper);
309  values[5] = UInt16GetDatum(page->pd_special);
310  values[6] = UInt16GetDatum(PageGetPageSize(page));
311  break;
312  case INT4OID:
313  Assert(TupleDescAttr(tupdesc, 4)->atttypid == INT4OID &&
314  TupleDescAttr(tupdesc, 5)->atttypid == INT4OID &&
315  TupleDescAttr(tupdesc, 6)->atttypid == INT4OID);
316  values[3] = Int32GetDatum(page->pd_lower);
317  values[4] = Int32GetDatum(page->pd_upper);
318  values[5] = Int32GetDatum(page->pd_special);
319  values[6] = Int32GetDatum(PageGetPageSize(page));
320  break;
321  default:
322  elog(ERROR, "incorrect output types");
323  break;
324  }
325 
326  values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page));
327  values[8] = TransactionIdGetDatum(page->pd_prune_xid);
328 
329  /* Build and return the tuple. */
330 
331  memset(nulls, 0, sizeof(nulls));
332 
333  tuple = heap_form_tuple(tupdesc, values, nulls);
334  result = HeapTupleGetDatum(tuple);
335 
336  PG_RETURN_DATUM(result);
337 }
338 
339 /*
340  * page_checksum
341  *
342  * Compute checksum of a raw page
343  */
344 
347 
348 static Datum
350 {
351  bytea *raw_page = PG_GETARG_BYTEA_P(0);
352  int64 blkno = (ext_version == PAGEINSPECT_V1_8 ? PG_GETARG_UINT32(1) : PG_GETARG_INT64(1));
353  int raw_page_size;
354  PageHeader page;
355 
356  if (!superuser())
357  ereport(ERROR,
358  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
359  errmsg("must be superuser to use raw page functions")));
360 
361  if (blkno < 0 || blkno > MaxBlockNumber)
362  ereport(ERROR,
363  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
364  errmsg("invalid block number")));
365 
366  raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
367 
368  /*
369  * Check that the supplied page is of the right size.
370  */
371  if (raw_page_size != BLCKSZ)
372  ereport(ERROR,
373  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
374  errmsg("incorrect size of input page (%d bytes)", raw_page_size)));
375 
376  page = (PageHeader) VARDATA(raw_page);
377 
378  PG_RETURN_INT16(pg_checksum_page((char *) page, blkno));
379 }
380 
381 Datum
383 {
385 }
386 
387 /*
388  * Entry point for old extension version
389  */
390 Datum
392 {
394 }
#define PG_GETARG_UINT32(n)
Definition: fmgr.h:270
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
Page get_page_from_raw(bytea *raw_page)
Definition: rawpage.c:215
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:207
int errhint(const char *fmt,...)
Definition: elog.c:1156
#define VARDATA_ANY(PTR)
Definition: postgres.h:361
#define VARDATA(PTR)
Definition: postgres.h:315
uint16 pd_flags
Definition: bufpage.h:157
#define VARSIZE(PTR)
Definition: postgres.h:316
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define VARHDRSZ
Definition: c.h:627
PG_MODULE_MAGIC
Definition: rawpage.c:32
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:741
#define AccessShareLock
Definition: lockdefs.h:36
#define PG_RETURN_INT16(x)
Definition: fmgr.h:356
int errcode(int sqlerrcode)
Definition: elog.c:698
#define LSNGetDatum(X)
Definition: pg_lsn.h:22
bool superuser(void)
Definition: superuser.c:46
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3772
static bytea * get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
Definition: rawpage.c:142
RangeVar * makeRangeVarFromNameList(List *names)
Definition: namespace.c:3107
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:371
#define SizeOfPageHeaderData
Definition: bufpage.h:216
Form_pg_class rd_rel
Definition: rel.h:109
NameData relname
Definition: pg_class.h:38
static Datum page_checksum_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
Definition: rawpage.c:349
ForkNumber forkname_to_number(const char *forkName)
Definition: relpath.c:50
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
uint16 pd_checksum
Definition: bufpage.h:156
#define PG_GETARG_BYTEA_P(n)
Definition: fmgr.h:335
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:43
#define ERROR
Definition: elog.h:46
#define MaxBlockNumber
Definition: block.h:35
Datum page_header(PG_FUNCTION_ARGS)
Definition: rawpage.c:246
static char * buf
Definition: pg_test_fsync.c:68
#define PageGetPageLayoutVersion(page)
Definition: bufpage.h:275
int errdetail(const char *fmt,...)
Definition: elog.c:1042
#define PageGetPageSize(page)
Definition: bufpage.h:268
#define RelationGetRelationName(relation)
Definition: rel.h:511
unsigned int uint32
Definition: c.h:441
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
ForkNumber
Definition: relpath.h:40
List * textToQualifiedNameList(text *textval)
Definition: varlena.c:3695
LocationIndex pd_special
Definition: bufpage.h:160
#define TransactionIdGetDatum(X)
Definition: postgres.h:565
Datum get_raw_page_fork(PG_FUNCTION_ARGS)
Definition: rawpage.c:123
uintptr_t Datum
Definition: postgres.h:411
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4011
Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: relation.c:138
#define ereport(elevel,...)
Definition: elog.h:157
Datum get_raw_page(PG_FUNCTION_ARGS)
Definition: rawpage.c:68
BlockNumber RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
Definition: bufmgr.c:2939
Datum get_raw_page_1_9(PG_FUNCTION_ARGS)
Definition: rawpage.c:46
PageHeaderData * PageHeader
Definition: bufpage.h:166
Datum page_checksum_1_9(PG_FUNCTION_ARGS)
Definition: rawpage.c:382
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
TransactionId pd_prune_xid
Definition: bufpage.h:162
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:631
#define PG_NARGS()
Definition: fmgr.h:203
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:220
PG_FUNCTION_INFO_V1(get_raw_page_1_9)
Datum get_raw_page_fork_1_9(PG_FUNCTION_ARGS)
Definition: rawpage.c:97
#define PageGetLSN(page)
Definition: bufpage.h:366
static Datum values[MAXATTR]
Definition: bootstrap.c:166
char * text_to_cstring(const text *t)
Definition: varlena.c:223
#define Int32GetDatum(X)
Definition: postgres.h:523
#define VARSIZE_ANY_EXHDR(PTR)
Definition: postgres.h:354
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
#define CStringGetTextDatum(s)
Definition: builtins.h:82
Definition: c.h:621
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:342
LocationIndex pd_upper
Definition: bufpage.h:159
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define snprintf
Definition: port.h:216
int Buffer
Definition: buf.h:23
#define UInt16GetDatum(X)
Definition: postgres.h:509
uint16 pg_checksum_page(char *page, BlockNumber blkno)
Pointer Page
Definition: bufpage.h:78
Datum page_checksum(PG_FUNCTION_ARGS)
Definition: rawpage.c:391
LocationIndex pd_lower
Definition: bufpage.h:158
pageinspect_version
Definition: pageinspect.h:21