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