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
32 PG_MODULE_MAGIC;
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 */
43 PG_FUNCTION_INFO_V1(get_raw_page);
44
45 Datum
get_raw_page(PG_FUNCTION_ARGS)46 get_raw_page(PG_FUNCTION_ARGS)
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 */
72 PG_FUNCTION_INFO_V1(get_raw_page_fork);
73
74 Datum
get_raw_page_fork(PG_FUNCTION_ARGS)75 get_raw_page_fork(PG_FUNCTION_ARGS)
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 *
get_raw_page_internal(text * relname,ForkNumber forknum,BlockNumber blkno)94 get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
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
107 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
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);
161 LockBuffer(buf, BUFFER_LOCK_SHARE);
162
163 memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
164
165 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
166 ReleaseBuffer(buf);
167
168 relation_close(rel, AccessShareLock);
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
get_page_from_raw(bytea * raw_page)187 get_page_from_raw(bytea *raw_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
215 PG_FUNCTION_INFO_V1(page_header);
216
217 Datum
page_header(PG_FUNCTION_ARGS)218 page_header(PG_FUNCTION_ARGS)
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
295 PG_FUNCTION_INFO_V1(page_checksum);
296
297 Datum
page_checksum(PG_FUNCTION_ARGS)298 page_checksum(PG_FUNCTION_ARGS)
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 }
324