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-2018, PostgreSQL Global Development Group
9  *
10  * IDENTIFICATION
11  * contrib/pageinspect/rawpage.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 
16 #include "postgres.h"
17 
18 #include "pageinspect.h"
19 
20 #include "access/htup_details.h"
21 #include "catalog/namespace.h"
22 #include "catalog/pg_type.h"
23 #include "funcapi.h"
24 #include "miscadmin.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 
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  */
44 
45 Datum
47 {
48  text *relname = PG_GETARG_TEXT_PP(0);
49  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  if (PG_NARGS() != 2)
58  ereport(ERROR,
59  (errmsg("wrong number of arguments to get_raw_page()"),
60  errhint("Run the updated pageinspect.sql script.")));
61 
62  raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
63 
64  PG_RETURN_BYTEA_P(raw_page);
65 }
66 
67 /*
68  * get_raw_page_fork
69  *
70  * Same, for any fork
71  */
73 
74 Datum
76 {
77  text *relname = PG_GETARG_TEXT_PP(0);
78  text *forkname = PG_GETARG_TEXT_PP(1);
79  uint32 blkno = PG_GETARG_UINT32(2);
80  bytea *raw_page;
81  ForkNumber forknum;
82 
83  forknum = forkname_to_number(text_to_cstring(forkname));
84 
85  raw_page = get_raw_page_internal(relname, forknum, blkno);
86 
87  PG_RETURN_BYTEA_P(raw_page);
88 }
89 
90 /*
91  * workhorse
92  */
93 static bytea *
95 {
96  bytea *raw_page;
97  RangeVar *relrv;
98  Relation rel;
99  char *raw_page_data;
100  Buffer buf;
101 
102  if (!superuser())
103  ereport(ERROR,
104  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
105  (errmsg("must be superuser to use raw functions"))));
106 
108  rel = relation_openrv(relrv, AccessShareLock);
109 
110  /* Check that this relation has storage */
111  if (rel->rd_rel->relkind == RELKIND_VIEW)
112  ereport(ERROR,
113  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
114  errmsg("cannot get raw page from view \"%s\"",
115  RelationGetRelationName(rel))));
116  if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
117  ereport(ERROR,
118  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
119  errmsg("cannot get raw page from composite type \"%s\"",
120  RelationGetRelationName(rel))));
121  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
122  ereport(ERROR,
123  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
124  errmsg("cannot get raw page from foreign table \"%s\"",
125  RelationGetRelationName(rel))));
126  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
127  ereport(ERROR,
128  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
129  errmsg("cannot get raw page from partitioned table \"%s\"",
130  RelationGetRelationName(rel))));
131  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
132  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  if (RELATION_IS_OTHER_TEMP(rel))
143  ereport(ERROR,
144  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
145  errmsg("cannot access temporary tables of other sessions")));
146 
147  if (blkno >= RelationGetNumberOfBlocksInFork(rel, forknum))
148  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  raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
155  SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
156  raw_page_data = VARDATA(raw_page);
157 
158  /* Take a verbatim copy of the page */
159 
160  buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
162 
163  memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
164 
166  ReleaseBuffer(buf);
167 
169 
170  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
188 {
189  Page page;
190  int raw_page_size;
191 
192  raw_page_size = VARSIZE_ANY_EXHDR(raw_page);
193 
194  if (raw_page_size != BLCKSZ)
195  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  page = palloc(raw_page_size);
202 
203  memcpy(page, VARDATA_ANY(raw_page), raw_page_size);
204 
205  return page;
206 }
207 
208 
209 /*
210  * page_header
211  *
212  * Allows inspection of page header fields of a raw page
213  */
214 
216 
217 Datum
219 {
220  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  if (!superuser())
234  ereport(ERROR,
235  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
236  (errmsg("must be superuser to use raw page functions"))));
237 
238  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  if (raw_page_size < SizeOfPageHeaderData)
245  ereport(ERROR,
246  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
247  errmsg("input page too small (%d bytes)", raw_page_size)));
248 
249  page = (PageHeader) VARDATA(raw_page);
250 
251  /* Build a tuple descriptor for our result type */
252  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
253  elog(ERROR, "return type must be a row type");
254 
255  /* Extract information from the page header */
256 
257  lsn = PageGetLSN(page);
258 
259  /* pageinspect >= 1.2 uses pg_lsn instead of text for the LSN field. */
260  if (TupleDescAttr(tupdesc, 0)->atttypid == TEXTOID)
261  {
262  char lsnchar[64];
263 
264  snprintf(lsnchar, sizeof(lsnchar), "%X/%X",
265  (uint32) (lsn >> 32), (uint32) lsn);
266  values[0] = CStringGetTextDatum(lsnchar);
267  }
268  else
269  values[0] = LSNGetDatum(lsn);
270  values[1] = UInt16GetDatum(page->pd_checksum);
271  values[2] = UInt16GetDatum(page->pd_flags);
272  values[3] = UInt16GetDatum(page->pd_lower);
273  values[4] = UInt16GetDatum(page->pd_upper);
274  values[5] = UInt16GetDatum(page->pd_special);
275  values[6] = UInt16GetDatum(PageGetPageSize(page));
276  values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page));
277  values[8] = TransactionIdGetDatum(page->pd_prune_xid);
278 
279  /* Build and return the tuple. */
280 
281  memset(nulls, 0, sizeof(nulls));
282 
283  tuple = heap_form_tuple(tupdesc, values, nulls);
284  result = HeapTupleGetDatum(tuple);
285 
286  PG_RETURN_DATUM(result);
287 }
288 
289 /*
290  * page_checksum
291  *
292  * Compute checksum of a raw page
293  */
294 
296 
297 Datum
299 {
300  bytea *raw_page = PG_GETARG_BYTEA_P(0);
301  uint32 blkno = PG_GETARG_INT32(1);
302  int raw_page_size;
303  PageHeader page;
304 
305  if (!superuser())
306  ereport(ERROR,
307  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
308  (errmsg("must be superuser to use raw page functions"))));
309 
310  raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
311 
312  /*
313  * Check that the supplied page is of the right size.
314  */
315  if (raw_page_size != BLCKSZ)
316  ereport(ERROR,
317  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
318  errmsg("incorrect size of input page (%d bytes)", raw_page_size)));
319 
320  page = (PageHeader) VARDATA(raw_page);
321 
322  PG_RETURN_INT16(pg_checksum_page((char *) page, blkno));
323 }
#define PG_GETARG_UINT32(n)
Definition: fmgr.h:240
Page get_page_from_raw(bytea *raw_page)
Definition: rawpage.c:187
Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: heapam.c:1202
#define PG_GETARG_INT32(n)
Definition: fmgr.h:239
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:87
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:211
int errhint(const char *fmt,...)
Definition: elog.c:987
#define VARDATA_ANY(PTR)
Definition: postgres.h:348
#define VARDATA(PTR)
Definition: postgres.h:302
uint16 pd_flags
Definition: bufpage.h:153
#define VARSIZE(PTR)
Definition: postgres.h:303
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:93
#define VARHDRSZ
Definition: c.h:522
PG_MODULE_MAGIC
Definition: rawpage.c:32
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:640
#define AccessShareLock
Definition: lockdefs.h:36
#define PG_RETURN_INT16(x)
Definition: fmgr.h:321
int errcode(int sqlerrcode)
Definition: elog.c:575
#define LSNGetDatum(X)
Definition: pg_lsn.h:22
bool superuser(void)
Definition: superuser.c:47
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1270
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
static bytea * get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
Definition: rawpage.c:94
RangeVar * makeRangeVarFromNameList(List *names)
Definition: namespace.c:3042
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1074
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:335
#define SizeOfPageHeaderData
Definition: bufpage.h:212
Form_pg_class rd_rel
Definition: rel.h:84
ForkNumber forkname_to_number(const char *forkName)
Definition: relpath.c:47
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:278
uint16 pd_checksum
Definition: bufpage.h:152
#define PG_GETARG_BYTEA_P(n)
Definition: fmgr.h:304
#define ERROR
Definition: elog.h:43
Datum page_header(PG_FUNCTION_ARGS)
Definition: rawpage.c:218
static char * buf
Definition: pg_test_fsync.c:67
#define PageGetPageLayoutVersion(page)
Definition: bufpage.h:271
int errdetail(const char *fmt,...)
Definition: elog.c:873
#define PageGetPageSize(page)
Definition: bufpage.h:264
#define RelationGetRelationName(relation)
Definition: rel.h:441
unsigned int uint32
Definition: c.h:325
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
Oid atttypid
Definition: pg_attribute.h:49
#define ereport(elevel, rest)
Definition: elog.h:122
ForkNumber
Definition: relpath.h:40
List * textToQualifiedNameList(text *textval)
Definition: varlena.c:3232
LocationIndex pd_special
Definition: bufpage.h:156
#define TransactionIdGetDatum(X)
Definition: postgres.h:506
Datum get_raw_page_fork(PG_FUNCTION_ARGS)
Definition: rawpage.c:75
uintptr_t Datum
Definition: postgres.h:367
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:318
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
Datum get_raw_page(PG_FUNCTION_ARGS)
Definition: rawpage.c:46
BlockNumber RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
Definition: bufmgr.c:2788
PageHeaderData * PageHeader
Definition: bufpage.h:162
uint64 XLogRecPtr
Definition: xlogdefs.h:21
TransactionId pd_prune_xid
Definition: bufpage.h:158
PG_FUNCTION_INFO_V1(get_raw_page)
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:538
#define PG_NARGS()
Definition: fmgr.h:173
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:231
#define PageGetLSN(page)
Definition: bufpage.h:362
static Datum values[MAXATTR]
Definition: bootstrap.c:164
char * text_to_cstring(const text *t)
Definition: varlena.c:182
#define VARSIZE_ANY_EXHDR(PTR)
Definition: postgres.h:341
void * palloc(Size size)
Definition: mcxt.c:924
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:88
#define CStringGetTextDatum(s)
Definition: builtins.h:95
Definition: c.h:516
#define PG_FUNCTION_ARGS
Definition: fmgr.h:163
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:329
#define elog
Definition: elog.h:219
LocationIndex pd_upper
Definition: bufpage.h:155
int Buffer
Definition: buf.h:23
#define UInt16GetDatum(X)
Definition: postgres.h:450
uint16 pg_checksum_page(char *page, BlockNumber blkno)
Pointer Page
Definition: bufpage.h:74
Datum page_checksum(PG_FUNCTION_ARGS)
Definition: rawpage.c:298
LocationIndex pd_lower
Definition: bufpage.h:154