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-2017, 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/catalog.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
33 PG_MODULE_MAGIC;
34
35 static bytea *get_raw_page_internal(text *relname, ForkNumber forknum,
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 */
44 PG_FUNCTION_INFO_V1(get_raw_page);
45
46 Datum
get_raw_page(PG_FUNCTION_ARGS)47 get_raw_page(PG_FUNCTION_ARGS)
48 {
49 text *relname = PG_GETARG_TEXT_PP(0);
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 */
73 PG_FUNCTION_INFO_V1(get_raw_page_fork);
74
75 Datum
get_raw_page_fork(PG_FUNCTION_ARGS)76 get_raw_page_fork(PG_FUNCTION_ARGS)
77 {
78 text *relname = PG_GETARG_TEXT_PP(0);
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 *
get_raw_page_internal(text * relname,ForkNumber forknum,BlockNumber blkno)95 get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
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 functions"))));
107
108 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
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
133 /*
134 * Reject attempts to read non-local temporary relations; we would be
135 * likely to get wrong data since we have no visibility into the owning
136 * session's local buffers.
137 */
138 if (RELATION_IS_OTHER_TEMP(rel))
139 ereport(ERROR,
140 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
141 errmsg("cannot access temporary tables of other sessions")));
142
143 if (blkno >= RelationGetNumberOfBlocksInFork(rel, forknum))
144 ereport(ERROR,
145 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
146 errmsg("block number %u is out of range for relation \"%s\"",
147 blkno, RelationGetRelationName(rel))));
148
149 /* Initialize buffer to copy to */
150 raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
151 SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
152 raw_page_data = VARDATA(raw_page);
153
154 /* Take a verbatim copy of the page */
155
156 buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
157 LockBuffer(buf, BUFFER_LOCK_SHARE);
158
159 memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
160
161 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
162 ReleaseBuffer(buf);
163
164 relation_close(rel, AccessShareLock);
165
166 return raw_page;
167 }
168
169
170 /*
171 * get_page_from_raw
172 *
173 * Get a palloc'd, maxalign'ed page image from the result of get_raw_page()
174 *
175 * On machines with MAXALIGN = 8, the payload of a bytea is not maxaligned,
176 * since it will start 4 bytes into a palloc'd value. On alignment-picky
177 * machines, this will cause failures in accesses to 8-byte-wide values
178 * within the page. We don't need to worry if accessing only 4-byte or
179 * smaller fields, but when examining a struct that contains 8-byte fields,
180 * use this function for safety.
181 */
182 Page
get_page_from_raw(bytea * raw_page)183 get_page_from_raw(bytea *raw_page)
184 {
185 Page page;
186 int raw_page_size;
187
188 raw_page_size = VARSIZE_ANY_EXHDR(raw_page);
189
190 if (raw_page_size != BLCKSZ)
191 ereport(ERROR,
192 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
193 errmsg("invalid page size"),
194 errdetail("Expected %d bytes, got %d.",
195 BLCKSZ, raw_page_size)));
196
197 page = palloc(raw_page_size);
198
199 memcpy(page, VARDATA_ANY(raw_page), raw_page_size);
200
201 return page;
202 }
203
204
205 /*
206 * page_header
207 *
208 * Allows inspection of page header fields of a raw page
209 */
210
211 PG_FUNCTION_INFO_V1(page_header);
212
213 Datum
page_header(PG_FUNCTION_ARGS)214 page_header(PG_FUNCTION_ARGS)
215 {
216 bytea *raw_page = PG_GETARG_BYTEA_P(0);
217 int raw_page_size;
218
219 TupleDesc tupdesc;
220
221 Datum result;
222 HeapTuple tuple;
223 Datum values[9];
224 bool nulls[9];
225
226 PageHeader page;
227 XLogRecPtr lsn;
228
229 if (!superuser())
230 ereport(ERROR,
231 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
232 (errmsg("must be superuser to use raw page functions"))));
233
234 raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
235
236 /*
237 * Check that enough data was supplied, so that we don't try to access
238 * fields outside the supplied buffer.
239 */
240 if (raw_page_size < SizeOfPageHeaderData)
241 ereport(ERROR,
242 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
243 errmsg("input page too small (%d bytes)", raw_page_size)));
244
245 page = (PageHeader) VARDATA(raw_page);
246
247 /* Build a tuple descriptor for our result type */
248 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
249 elog(ERROR, "return type must be a row type");
250
251 /* Extract information from the page header */
252
253 lsn = PageGetLSN(page);
254
255 /* pageinspect >= 1.2 uses pg_lsn instead of text for the LSN field. */
256 if (tupdesc->attrs[0]->atttypid == TEXTOID)
257 {
258 char lsnchar[64];
259
260 snprintf(lsnchar, sizeof(lsnchar), "%X/%X",
261 (uint32) (lsn >> 32), (uint32) lsn);
262 values[0] = CStringGetTextDatum(lsnchar);
263 }
264 else
265 values[0] = LSNGetDatum(lsn);
266 values[1] = UInt16GetDatum(page->pd_checksum);
267 values[2] = UInt16GetDatum(page->pd_flags);
268 values[3] = UInt16GetDatum(page->pd_lower);
269 values[4] = UInt16GetDatum(page->pd_upper);
270 values[5] = UInt16GetDatum(page->pd_special);
271 values[6] = UInt16GetDatum(PageGetPageSize(page));
272 values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page));
273 values[8] = TransactionIdGetDatum(page->pd_prune_xid);
274
275 /* Build and return the tuple. */
276
277 memset(nulls, 0, sizeof(nulls));
278
279 tuple = heap_form_tuple(tupdesc, values, nulls);
280 result = HeapTupleGetDatum(tuple);
281
282 PG_RETURN_DATUM(result);
283 }
284
285 /*
286 * page_checksum
287 *
288 * Compute checksum of a raw page
289 */
290
291 PG_FUNCTION_INFO_V1(page_checksum);
292
293 Datum
page_checksum(PG_FUNCTION_ARGS)294 page_checksum(PG_FUNCTION_ARGS)
295 {
296 bytea *raw_page = PG_GETARG_BYTEA_P(0);
297 uint32 blkno = PG_GETARG_INT32(1);
298 int raw_page_size;
299 PageHeader page;
300
301 if (!superuser())
302 ereport(ERROR,
303 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
304 (errmsg("must be superuser to use raw page functions"))));
305
306 raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
307
308 /*
309 * Check that the supplied page is of the right size.
310 */
311 if (raw_page_size != BLCKSZ)
312 ereport(ERROR,
313 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
314 errmsg("incorrect size of input page (%d bytes)", raw_page_size)));
315
316 page = (PageHeader) VARDATA(raw_page);
317
318 PG_RETURN_INT16(pg_checksum_page((char *) page, blkno));
319 }
320