1 /*-------------------------------------------------------------------------
4 * Functions to investigate heap pages
6 * We check the input to these functions for corrupt pointers etc. that
7 * might cause crashes, but at the same time we try to print out as much
8 * information as possible, even if it's nonsense. That's because if a
9 * page is corrupt, we don't know why and how exactly it is corrupt, so we
10 * let the user to judge it.
12 * These functions are restricted to superusers for the fear of introducing
13 * security holes if the input checking isn't as water-tight as it should.
14 * You'd need to be superuser to obtain a raw page image anyway, so
15 * there's hardly any use case for using these without superuser-rights
18 * Copyright (c) 2007-2008, PostgreSQL Global Development Group
23 *-------------------------------------------------------------------------
30 #include "access/heapam.h"
31 #include "access/transam.h"
32 #include "catalog/namespace.h"
33 #include "catalog/pg_type.h"
34 #include "utils/builtins.h"
35 #include "miscadmin.h"
37 Datum
heap_page_items(PG_FUNCTION_ARGS
);
43 * Converts a bits8-array of 'len' bits to a human-readable
44 * c-string representation.
47 bits_to_text(bits8
*bits
, int len
)
52 str
= palloc(len
+ 1);
54 for (i
= 0; i
< len
; i
++)
55 str
[i
] = (bits
[(i
/ 8)] & (1 << (i
% 8))) ? '1' : '0';
66 * Allows inspection of line pointers and tuple headers of a heap page.
68 PG_FUNCTION_INFO_V1(heap_page_items
);
70 typedef struct heap_page_items_state
75 } heap_page_items_state
;
78 heap_page_items(PG_FUNCTION_ARGS
)
80 bytea
*raw_page
= PG_GETARG_BYTEA_P(0);
81 heap_page_items_state
*inter_call_data
= NULL
;
82 FuncCallContext
*fctx
;
87 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
88 (errmsg("must be superuser to use raw page functions"))));
90 raw_page_size
= VARSIZE(raw_page
) - VARHDRSZ
;
92 if (SRF_IS_FIRSTCALL())
97 if (raw_page_size
< SizeOfPageHeaderData
)
99 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
100 errmsg("input page too small (%d bytes)", raw_page_size
)));
102 fctx
= SRF_FIRSTCALL_INIT();
103 mctx
= MemoryContextSwitchTo(fctx
->multi_call_memory_ctx
);
105 inter_call_data
= palloc(sizeof(heap_page_items_state
));
107 /* Build a tuple descriptor for our result type */
108 if (get_call_result_type(fcinfo
, NULL
, &tupdesc
) != TYPEFUNC_COMPOSITE
)
109 elog(ERROR
, "return type must be a row type");
111 inter_call_data
->tupd
= tupdesc
;
113 inter_call_data
->offset
= FirstOffsetNumber
;
114 inter_call_data
->page
= VARDATA(raw_page
);
116 fctx
->max_calls
= PageGetMaxOffsetNumber(inter_call_data
->page
);
117 fctx
->user_fctx
= inter_call_data
;
119 MemoryContextSwitchTo(mctx
);
122 fctx
= SRF_PERCALL_SETUP();
123 inter_call_data
= fctx
->user_fctx
;
125 if (fctx
->call_cntr
< fctx
->max_calls
)
127 Page page
= inter_call_data
->page
;
128 HeapTuple resultTuple
;
137 memset(nulls
, 0, sizeof(nulls
));
139 /* Extract information from the line pointer */
141 id
= PageGetItemId(page
, inter_call_data
->offset
);
143 lp_offset
= ItemIdGetOffset(id
);
144 lp_flags
= ItemIdGetFlags(id
);
145 lp_len
= ItemIdGetLength(id
);
147 values
[0] = UInt16GetDatum(inter_call_data
->offset
);
148 values
[1] = UInt16GetDatum(lp_offset
);
149 values
[2] = UInt16GetDatum(lp_flags
);
150 values
[3] = UInt16GetDatum(lp_len
);
153 * We do just enough validity checking to make sure we don't reference
154 * data outside the page passed to us. The page could be corrupt in
155 * many other ways, but at least we won't crash.
157 if (ItemIdHasStorage(id
) &&
158 lp_len
>= sizeof(HeapTupleHeader
) &&
159 lp_offset
== MAXALIGN(lp_offset
) &&
160 lp_offset
+ lp_len
<= raw_page_size
)
162 HeapTupleHeader tuphdr
;
165 /* Extract information from the tuple header */
167 tuphdr
= (HeapTupleHeader
) PageGetItem(page
, id
);
169 values
[4] = UInt32GetDatum(HeapTupleHeaderGetXmin(tuphdr
));
170 values
[5] = UInt32GetDatum(HeapTupleHeaderGetXmax(tuphdr
));
171 values
[6] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr
)); /* shared with xvac */
172 values
[7] = PointerGetDatum(&tuphdr
->t_ctid
);
173 values
[8] = UInt16GetDatum(tuphdr
->t_infomask2
);
174 values
[9] = UInt16GetDatum(tuphdr
->t_infomask
);
175 values
[10] = UInt8GetDatum(tuphdr
->t_hoff
);
178 * We already checked that the item as is completely within the
179 * raw page passed to us, with the length given in the line
180 * pointer.. Let's check that t_hoff doesn't point over lp_len,
181 * before using it to access t_bits and oid.
183 if (tuphdr
->t_hoff
>= sizeof(HeapTupleHeader
) &&
184 tuphdr
->t_hoff
<= lp_len
)
186 if (tuphdr
->t_infomask
& HEAP_HASNULL
)
188 bits_len
= tuphdr
->t_hoff
-
189 (((char *) tuphdr
->t_bits
) -((char *) tuphdr
));
191 values
[11] = CStringGetTextDatum(
192 bits_to_text(tuphdr
->t_bits
, bits_len
* 8));
197 if (tuphdr
->t_infomask
& HEAP_HASOID
)
198 values
[12] = HeapTupleHeaderGetOid(tuphdr
);
211 * The line pointer is not used, or it's invalid. Set the rest of
216 for (i
= 4; i
<= 12; i
++)
220 /* Build and return the result tuple. */
221 resultTuple
= heap_form_tuple(inter_call_data
->tupd
, values
, nulls
);
222 result
= HeapTupleGetDatum(resultTuple
);
224 inter_call_data
->offset
++;
226 SRF_RETURN_NEXT(fctx
, result
);
229 SRF_RETURN_DONE(fctx
);