]> granicus.if.org Git - postgresql/blob - contrib/pageinspect/rawpage.c
Fix pageinspect's heap_page_item to return infomasks as 32 bit values
[postgresql] / contrib / pageinspect / rawpage.c
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-2011, PostgreSQL Global Development Group
9  *
10  * IDENTIFICATION
11  *        contrib/pageinspect/rawpage.c
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include "access/heapam.h"
19 #include "access/transam.h"
20 #include "catalog/catalog.h"
21 #include "catalog/namespace.h"
22 #include "catalog/pg_type.h"
23 #include "fmgr.h"
24 #include "funcapi.h"
25 #include "miscadmin.h"
26 #include "storage/bufmgr.h"
27 #include "utils/builtins.h"
28
29 PG_MODULE_MAGIC;
30
31 Datum           get_raw_page(PG_FUNCTION_ARGS);
32 Datum           get_raw_page_fork(PG_FUNCTION_ARGS);
33 Datum           page_header(PG_FUNCTION_ARGS);
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
47 get_raw_page(PG_FUNCTION_ARGS)
48 {
49         text       *relname = PG_GETARG_TEXT_P(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
76 get_raw_page_fork(PG_FUNCTION_ARGS)
77 {
78         text       *relname = PG_GETARG_TEXT_P(0);
79         text       *forkname = PG_GETARG_TEXT_P(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 *
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
128         /*
129          * Reject attempts to read non-local temporary relations; we would be
130          * likely to get wrong data since we have no visibility into the owning
131          * session's local buffers.
132          */
133         if (RELATION_IS_OTHER_TEMP(rel))
134                 ereport(ERROR,
135                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
136                                  errmsg("cannot access temporary tables of other sessions")));
137
138         if (blkno >= RelationGetNumberOfBlocks(rel))
139                 elog(ERROR, "block number %u is out of range for relation \"%s\"",
140                          blkno, RelationGetRelationName(rel));
141
142         /* Initialize buffer to copy to */
143         raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
144         SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
145         raw_page_data = VARDATA(raw_page);
146
147         /* Take a verbatim copy of the page */
148
149         buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
150         LockBuffer(buf, BUFFER_LOCK_SHARE);
151
152         memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
153
154         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
155         ReleaseBuffer(buf);
156
157         relation_close(rel, AccessShareLock);
158
159         return raw_page;
160 }
161
162 /*
163  * page_header
164  *
165  * Allows inspection of page header fields of a raw page
166  */
167
168 PG_FUNCTION_INFO_V1(page_header);
169
170 Datum
171 page_header(PG_FUNCTION_ARGS)
172 {
173         bytea      *raw_page = PG_GETARG_BYTEA_P(0);
174         int                     raw_page_size;
175
176         TupleDesc       tupdesc;
177
178         Datum           result;
179         HeapTuple       tuple;
180         Datum           values[9];
181         bool            nulls[9];
182
183         PageHeader      page;
184         XLogRecPtr      lsn;
185         char            lsnchar[64];
186
187         if (!superuser())
188                 ereport(ERROR,
189                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
190                                  (errmsg("must be superuser to use raw page functions"))));
191
192         raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
193
194         /*
195          * Check that enough data was supplied, so that we don't try to access
196          * fields outside the supplied buffer.
197          */
198         if (raw_page_size < sizeof(PageHeaderData))
199                 ereport(ERROR,
200                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
201                                  errmsg("input page too small (%d bytes)", raw_page_size)));
202
203         page = (PageHeader) VARDATA(raw_page);
204
205         /* Build a tuple descriptor for our result type */
206         if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
207                 elog(ERROR, "return type must be a row type");
208
209         /* Extract information from the page header */
210
211         lsn = PageGetLSN(page);
212         snprintf(lsnchar, sizeof(lsnchar), "%X/%X", lsn.xlogid, lsn.xrecoff);
213
214         values[0] = CStringGetTextDatum(lsnchar);
215         values[1] = UInt16GetDatum(PageGetTLI(page));
216         values[2] = UInt16GetDatum(page->pd_flags);
217         values[3] = UInt16GetDatum(page->pd_lower);
218         values[4] = UInt16GetDatum(page->pd_upper);
219         values[5] = UInt16GetDatum(page->pd_special);
220         values[6] = UInt16GetDatum(PageGetPageSize(page));
221         values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page));
222         values[8] = TransactionIdGetDatum(page->pd_prune_xid);
223
224         /* Build and return the tuple. */
225
226         memset(nulls, 0, sizeof(nulls));
227
228         tuple = heap_form_tuple(tupdesc, values, nulls);
229         result = HeapTupleGetDatum(tuple);
230
231         PG_RETURN_DATUM(result);
232 }