Skip to content

Commit 193f5f9

Browse files
committed
pageinspect: Add bt_page_items function with bytea argument
Author: Tomas Vondra <tomas.vondra@2ndquadrant.com> Reviewed-by: Ashutosh Sharma <ashu.coek88@gmail.com>
1 parent 5ebeb57 commit 193f5f9

File tree

6 files changed

+211
-51
lines changed

6 files changed

+211
-51
lines changed

contrib/pageinspect/btreefuncs.c

Lines changed: 147 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
PG_FUNCTION_INFO_V1(bt_metap);
4343
PG_FUNCTION_INFO_V1(bt_page_items);
44+
PG_FUNCTION_INFO_V1(bt_page_items_bytea);
4445
PG_FUNCTION_INFO_V1(bt_page_stats);
4546

4647
#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
@@ -235,14 +236,6 @@ bt_page_stats(PG_FUNCTION_ARGS)
235236
PG_RETURN_DATUM(result);
236237
}
237238

238-
/*-------------------------------------------------------
239-
* bt_page_items()
240-
*
241-
* Get IndexTupleData set in a btree page
242-
*
243-
* Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
244-
*-------------------------------------------------------
245-
*/
246239

247240
/*
248241
* cross-call data structure for SRF
@@ -253,14 +246,72 @@ struct user_args
253246
OffsetNumber offset;
254247
};
255248

249+
/*-------------------------------------------------------
250+
* bt_page_print_tuples()
251+
*
252+
* Form a tuple describing index tuple at a given offset
253+
* ------------------------------------------------------
254+
*/
255+
static Datum
256+
bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset)
257+
{
258+
char *values[6];
259+
HeapTuple tuple;
260+
ItemId id;
261+
IndexTuple itup;
262+
int j;
263+
int off;
264+
int dlen;
265+
char *dump;
266+
char *ptr;
267+
268+
id = PageGetItemId(page, offset);
269+
270+
if (!ItemIdIsValid(id))
271+
elog(ERROR, "invalid ItemId");
272+
273+
itup = (IndexTuple) PageGetItem(page, id);
274+
275+
j = 0;
276+
values[j++] = psprintf("%d", offset);
277+
values[j++] = psprintf("(%u,%u)",
278+
ItemPointerGetBlockNumberNoCheck(&itup->t_tid),
279+
ItemPointerGetOffsetNumberNoCheck(&itup->t_tid));
280+
values[j++] = psprintf("%d", (int) IndexTupleSize(itup));
281+
values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f');
282+
values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');
283+
284+
ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
285+
dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
286+
dump = palloc0(dlen * 3 + 1);
287+
values[j] = dump;
288+
for (off = 0; off < dlen; off++)
289+
{
290+
if (off > 0)
291+
*dump++ = ' ';
292+
sprintf(dump, "%02x", *(ptr + off) & 0xff);
293+
dump += 2;
294+
}
295+
296+
tuple = BuildTupleFromCStrings(fctx->attinmeta, values);
297+
298+
return HeapTupleGetDatum(tuple);
299+
}
300+
301+
/*-------------------------------------------------------
302+
* bt_page_items()
303+
*
304+
* Get IndexTupleData set in a btree page
305+
*
306+
* Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
307+
*-------------------------------------------------------
308+
*/
256309
Datum
257310
bt_page_items(PG_FUNCTION_ARGS)
258311
{
259312
text *relname = PG_GETARG_TEXT_PP(0);
260313
uint32 blkno = PG_GETARG_UINT32(1);
261314
Datum result;
262-
char *values[6];
263-
HeapTuple tuple;
264315
FuncCallContext *fctx;
265316
MemoryContext mctx;
266317
struct user_args *uargs;
@@ -345,47 +396,8 @@ bt_page_items(PG_FUNCTION_ARGS)
345396

346397
if (fctx->call_cntr < fctx->max_calls)
347398
{
348-
ItemId id;
349-
IndexTuple itup;
350-
int j;
351-
int off;
352-
int dlen;
353-
char *dump;
354-
char *ptr;
355-
356-
id = PageGetItemId(uargs->page, uargs->offset);
357-
358-
if (!ItemIdIsValid(id))
359-
elog(ERROR, "invalid ItemId");
360-
361-
itup = (IndexTuple) PageGetItem(uargs->page, id);
362-
363-
j = 0;
364-
values[j++] = psprintf("%d", uargs->offset);
365-
values[j++] = psprintf("(%u,%u)",
366-
ItemPointerGetBlockNumberNoCheck(&itup->t_tid),
367-
ItemPointerGetOffsetNumberNoCheck(&itup->t_tid));
368-
values[j++] = psprintf("%d", (int) IndexTupleSize(itup));
369-
values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f');
370-
values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');
371-
372-
ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
373-
dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
374-
dump = palloc0(dlen * 3 + 1);
375-
values[j] = dump;
376-
for (off = 0; off < dlen; off++)
377-
{
378-
if (off > 0)
379-
*dump++ = ' ';
380-
sprintf(dump, "%02x", *(ptr + off) & 0xff);
381-
dump += 2;
382-
}
383-
384-
tuple = BuildTupleFromCStrings(fctx->attinmeta, values);
385-
result = HeapTupleGetDatum(tuple);
386-
387-
uargs->offset = uargs->offset + 1;
388-
399+
result = bt_page_print_tuples(fctx, uargs->page, uargs->offset);
400+
uargs->offset++;
389401
SRF_RETURN_NEXT(fctx, result);
390402
}
391403
else
@@ -396,6 +408,90 @@ bt_page_items(PG_FUNCTION_ARGS)
396408
}
397409
}
398410

411+
/*-------------------------------------------------------
412+
* bt_page_items_bytea()
413+
*
414+
* Get IndexTupleData set in a btree page
415+
*
416+
* Usage: SELECT * FROM bt_page_items(get_raw_page('t1_pkey', 1));
417+
*-------------------------------------------------------
418+
*/
419+
420+
Datum
421+
bt_page_items_bytea(PG_FUNCTION_ARGS)
422+
{
423+
bytea *raw_page = PG_GETARG_BYTEA_P(0);
424+
Datum result;
425+
FuncCallContext *fctx;
426+
struct user_args *uargs;
427+
int raw_page_size;
428+
429+
if (!superuser())
430+
ereport(ERROR,
431+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
432+
(errmsg("must be superuser to use pageinspect functions"))));
433+
434+
if (SRF_IS_FIRSTCALL())
435+
{
436+
BTPageOpaque opaque;
437+
MemoryContext mctx;
438+
TupleDesc tupleDesc;
439+
440+
raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
441+
442+
if (raw_page_size < SizeOfPageHeaderData)
443+
ereport(ERROR,
444+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
445+
errmsg("input page too small (%d bytes)", raw_page_size)));
446+
447+
fctx = SRF_FIRSTCALL_INIT();
448+
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
449+
450+
uargs = palloc(sizeof(struct user_args));
451+
452+
uargs->page = VARDATA(raw_page);
453+
454+
uargs->offset = FirstOffsetNumber;
455+
456+
opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
457+
458+
if (P_ISMETA(opaque))
459+
ereport(ERROR,
460+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
461+
errmsg("block is a meta page")));
462+
463+
if (P_ISDELETED(opaque))
464+
elog(NOTICE, "page is deleted");
465+
466+
fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
467+
468+
/* Build a tuple descriptor for our result type */
469+
if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
470+
elog(ERROR, "return type must be a row type");
471+
472+
fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc);
473+
474+
fctx->user_fctx = uargs;
475+
476+
MemoryContextSwitchTo(mctx);
477+
}
478+
479+
fctx = SRF_PERCALL_SETUP();
480+
uargs = fctx->user_fctx;
481+
482+
if (fctx->call_cntr < fctx->max_calls)
483+
{
484+
result = bt_page_print_tuples(fctx, uargs->page, uargs->offset);
485+
uargs->offset++;
486+
SRF_RETURN_NEXT(fctx, result);
487+
}
488+
else
489+
{
490+
pfree(uargs);
491+
SRF_RETURN_DONE(fctx);
492+
}
493+
}
494+
399495

400496
/* ------------------------------------------------
401497
* bt_metap()

contrib/pageinspect/expected/btree.out

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,17 @@ data | 01 00 00 00 00 00 00 01
4242

4343
SELECT * FROM bt_page_items('test1_a_idx', 2);
4444
ERROR: block number out of range
45+
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0));
46+
ERROR: block is a meta page
47+
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 1));
48+
-[ RECORD 1 ]-----------------------
49+
itemoffset | 1
50+
ctid | (0,1)
51+
itemlen | 16
52+
nulls | f
53+
vars | f
54+
data | 01 00 00 00 00 00 00 01
55+
56+
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 2));
57+
ERROR: block number 2 is out of range for relation "test1_a_idx"
4558
DROP TABLE test1;

contrib/pageinspect/pageinspect--1.5--1.6.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,17 @@ CREATE FUNCTION page_checksum(IN page bytea, IN blkno int4)
8383
RETURNS smallint
8484
AS 'MODULE_PATHNAME', 'page_checksum'
8585
LANGUAGE C STRICT PARALLEL SAFE;
86+
87+
--
88+
-- bt_page_items_bytea()
89+
--
90+
CREATE FUNCTION bt_page_items(IN page bytea,
91+
OUT itemoffset smallint,
92+
OUT ctid tid,
93+
OUT itemlen smallint,
94+
OUT nulls bool,
95+
OUT vars bool,
96+
OUT data text)
97+
RETURNS SETOF record
98+
AS 'MODULE_PATHNAME', 'bt_page_items_bytea'
99+
LANGUAGE C STRICT PARALLEL SAFE;

contrib/pageinspect/sql/btree.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,8 @@ SELECT * FROM bt_page_items('test1_a_idx', 0);
1414
SELECT * FROM bt_page_items('test1_a_idx', 1);
1515
SELECT * FROM bt_page_items('test1_a_idx', 2);
1616

17+
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0));
18+
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 1));
19+
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 2));
20+
1721
DROP TABLE test1;

doc/src/sgml/pageinspect.sgml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,38 @@ test=# SELECT * FROM bt_page_items('pg_cast_oid_index', 1);
333333
</para>
334334
</listitem>
335335
</varlistentry>
336+
337+
<varlistentry>
338+
<term>
339+
<function>bt_page_items(page bytea) returns setof record</function>
340+
<indexterm>
341+
<primary>bt_page_items</primary>
342+
</indexterm>
343+
</term>
344+
345+
<listitem>
346+
<para>
347+
It is also possible to pass a page to <function>bt_page_items</function>
348+
as a <type>bytea</> value. A page image obtained
349+
with <function>get_raw_page</function> should be passed as argument. So
350+
the last example could also be rewritten like this:
351+
<screen>
352+
test=# SELECT * FROM bt_page_items(get_raw_page('pg_cast_oid_index', 1));
353+
itemoffset | ctid | itemlen | nulls | vars | data
354+
------------+---------+---------+-------+------+-------------
355+
1 | (0,1) | 12 | f | f | 23 27 00 00
356+
2 | (0,2) | 12 | f | f | 24 27 00 00
357+
3 | (0,3) | 12 | f | f | 25 27 00 00
358+
4 | (0,4) | 12 | f | f | 26 27 00 00
359+
5 | (0,5) | 12 | f | f | 27 27 00 00
360+
6 | (0,6) | 12 | f | f | 28 27 00 00
361+
7 | (0,7) | 12 | f | f | 29 27 00 00
362+
8 | (0,8) | 12 | f | f | 2a 27 00 00
363+
</screen>
364+
All the other details are the same as explained in the previous item.
365+
</para>
366+
</listitem>
367+
</varlistentry>
336368
</variablelist>
337369
</sect2>
338370

src/include/access/nbtree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ typedef struct BTMetaPageData
176176
#define P_ISLEAF(opaque) ((opaque)->btpo_flags & BTP_LEAF)
177177
#define P_ISROOT(opaque) ((opaque)->btpo_flags & BTP_ROOT)
178178
#define P_ISDELETED(opaque) ((opaque)->btpo_flags & BTP_DELETED)
179+
#define P_ISMETA(opaque) ((opaque)->btpo_flags & BTP_META)
179180
#define P_ISHALFDEAD(opaque) ((opaque)->btpo_flags & BTP_HALF_DEAD)
180181
#define P_IGNORE(opaque) ((opaque)->btpo_flags & (BTP_DELETED|BTP_HALF_DEAD))
181182
#define P_HAS_GARBAGE(opaque) ((opaque)->btpo_flags & BTP_HAS_GARBAGE)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy