Skip to content

Commit

Permalink
pageinspect: Add bt_page_items function with bytea argument
Browse files Browse the repository at this point in the history
Author: Tomas Vondra <[email protected]>
Reviewed-by: Ashutosh Sharma <[email protected]>
  • Loading branch information
petere committed Apr 5, 2017
1 parent 5ebeb57 commit 193f5f9
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 51 deletions.
198 changes: 147 additions & 51 deletions contrib/pageinspect/btreefuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

PG_FUNCTION_INFO_V1(bt_metap);
PG_FUNCTION_INFO_V1(bt_page_items);
PG_FUNCTION_INFO_V1(bt_page_items_bytea);
PG_FUNCTION_INFO_V1(bt_page_stats);

#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
Expand Down Expand Up @@ -235,14 +236,6 @@ bt_page_stats(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(result);
}

/*-------------------------------------------------------
* bt_page_items()
*
* Get IndexTupleData set in a btree page
*
* Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
*-------------------------------------------------------
*/

/*
* cross-call data structure for SRF
Expand All @@ -253,14 +246,72 @@ struct user_args
OffsetNumber offset;
};

/*-------------------------------------------------------
* bt_page_print_tuples()
*
* Form a tuple describing index tuple at a given offset
* ------------------------------------------------------
*/
static Datum
bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset)
{
char *values[6];
HeapTuple tuple;
ItemId id;
IndexTuple itup;
int j;
int off;
int dlen;
char *dump;
char *ptr;

id = PageGetItemId(page, offset);

if (!ItemIdIsValid(id))
elog(ERROR, "invalid ItemId");

itup = (IndexTuple) PageGetItem(page, id);

j = 0;
values[j++] = psprintf("%d", offset);
values[j++] = psprintf("(%u,%u)",
ItemPointerGetBlockNumberNoCheck(&itup->t_tid),
ItemPointerGetOffsetNumberNoCheck(&itup->t_tid));
values[j++] = psprintf("%d", (int) IndexTupleSize(itup));
values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f');
values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');

ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
dump = palloc0(dlen * 3 + 1);
values[j] = dump;
for (off = 0; off < dlen; off++)
{
if (off > 0)
*dump++ = ' ';
sprintf(dump, "%02x", *(ptr + off) & 0xff);
dump += 2;
}

tuple = BuildTupleFromCStrings(fctx->attinmeta, values);

return HeapTupleGetDatum(tuple);
}

/*-------------------------------------------------------
* bt_page_items()
*
* Get IndexTupleData set in a btree page
*
* Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
*-------------------------------------------------------
*/
Datum
bt_page_items(PG_FUNCTION_ARGS)
{
text *relname = PG_GETARG_TEXT_PP(0);
uint32 blkno = PG_GETARG_UINT32(1);
Datum result;
char *values[6];
HeapTuple tuple;
FuncCallContext *fctx;
MemoryContext mctx;
struct user_args *uargs;
Expand Down Expand Up @@ -345,47 +396,8 @@ bt_page_items(PG_FUNCTION_ARGS)

if (fctx->call_cntr < fctx->max_calls)
{
ItemId id;
IndexTuple itup;
int j;
int off;
int dlen;
char *dump;
char *ptr;

id = PageGetItemId(uargs->page, uargs->offset);

if (!ItemIdIsValid(id))
elog(ERROR, "invalid ItemId");

itup = (IndexTuple) PageGetItem(uargs->page, id);

j = 0;
values[j++] = psprintf("%d", uargs->offset);
values[j++] = psprintf("(%u,%u)",
ItemPointerGetBlockNumberNoCheck(&itup->t_tid),
ItemPointerGetOffsetNumberNoCheck(&itup->t_tid));
values[j++] = psprintf("%d", (int) IndexTupleSize(itup));
values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f');
values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');

ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
dump = palloc0(dlen * 3 + 1);
values[j] = dump;
for (off = 0; off < dlen; off++)
{
if (off > 0)
*dump++ = ' ';
sprintf(dump, "%02x", *(ptr + off) & 0xff);
dump += 2;
}

tuple = BuildTupleFromCStrings(fctx->attinmeta, values);
result = HeapTupleGetDatum(tuple);

uargs->offset = uargs->offset + 1;

result = bt_page_print_tuples(fctx, uargs->page, uargs->offset);
uargs->offset++;
SRF_RETURN_NEXT(fctx, result);
}
else
Expand All @@ -396,6 +408,90 @@ bt_page_items(PG_FUNCTION_ARGS)
}
}

/*-------------------------------------------------------
* bt_page_items_bytea()
*
* Get IndexTupleData set in a btree page
*
* Usage: SELECT * FROM bt_page_items(get_raw_page('t1_pkey', 1));
*-------------------------------------------------------
*/

Datum
bt_page_items_bytea(PG_FUNCTION_ARGS)
{
bytea *raw_page = PG_GETARG_BYTEA_P(0);
Datum result;
FuncCallContext *fctx;
struct user_args *uargs;
int raw_page_size;

if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to use pageinspect functions"))));

if (SRF_IS_FIRSTCALL())
{
BTPageOpaque opaque;
MemoryContext mctx;
TupleDesc tupleDesc;

raw_page_size = VARSIZE(raw_page) - VARHDRSZ;

if (raw_page_size < SizeOfPageHeaderData)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("input page too small (%d bytes)", raw_page_size)));

fctx = SRF_FIRSTCALL_INIT();
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);

uargs = palloc(sizeof(struct user_args));

uargs->page = VARDATA(raw_page);

uargs->offset = FirstOffsetNumber;

opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);

if (P_ISMETA(opaque))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("block is a meta page")));

if (P_ISDELETED(opaque))
elog(NOTICE, "page is deleted");

fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);

/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");

fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc);

fctx->user_fctx = uargs;

MemoryContextSwitchTo(mctx);
}

fctx = SRF_PERCALL_SETUP();
uargs = fctx->user_fctx;

if (fctx->call_cntr < fctx->max_calls)
{
result = bt_page_print_tuples(fctx, uargs->page, uargs->offset);
uargs->offset++;
SRF_RETURN_NEXT(fctx, result);
}
else
{
pfree(uargs);
SRF_RETURN_DONE(fctx);
}
}


/* ------------------------------------------------
* bt_metap()
Expand Down
13 changes: 13 additions & 0 deletions contrib/pageinspect/expected/btree.out
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,17 @@ data | 01 00 00 00 00 00 00 01

SELECT * FROM bt_page_items('test1_a_idx', 2);
ERROR: block number out of range
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0));
ERROR: block is a meta page
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 1));
-[ RECORD 1 ]-----------------------
itemoffset | 1
ctid | (0,1)
itemlen | 16
nulls | f
vars | f
data | 01 00 00 00 00 00 00 01

SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 2));
ERROR: block number 2 is out of range for relation "test1_a_idx"
DROP TABLE test1;
14 changes: 14 additions & 0 deletions contrib/pageinspect/pageinspect--1.5--1.6.sql
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,17 @@ CREATE FUNCTION page_checksum(IN page bytea, IN blkno int4)
RETURNS smallint
AS 'MODULE_PATHNAME', 'page_checksum'
LANGUAGE C STRICT PARALLEL SAFE;

--
-- bt_page_items_bytea()
--
CREATE FUNCTION bt_page_items(IN page bytea,
OUT itemoffset smallint,
OUT ctid tid,
OUT itemlen smallint,
OUT nulls bool,
OUT vars bool,
OUT data text)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'bt_page_items_bytea'
LANGUAGE C STRICT PARALLEL SAFE;
4 changes: 4 additions & 0 deletions contrib/pageinspect/sql/btree.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ SELECT * FROM bt_page_items('test1_a_idx', 0);
SELECT * FROM bt_page_items('test1_a_idx', 1);
SELECT * FROM bt_page_items('test1_a_idx', 2);

SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0));
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 1));
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 2));

DROP TABLE test1;
32 changes: 32 additions & 0 deletions doc/src/sgml/pageinspect.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,38 @@ test=# SELECT * FROM bt_page_items('pg_cast_oid_index', 1);
</para>
</listitem>
</varlistentry>

<varlistentry>
<term>
<function>bt_page_items(page bytea) returns setof record</function>
<indexterm>
<primary>bt_page_items</primary>
</indexterm>
</term>

<listitem>
<para>
It is also possible to pass a page to <function>bt_page_items</function>
as a <type>bytea</> value. A page image obtained
with <function>get_raw_page</function> should be passed as argument. So
the last example could also be rewritten like this:
<screen>
test=# SELECT * FROM bt_page_items(get_raw_page('pg_cast_oid_index', 1));
itemoffset | ctid | itemlen | nulls | vars | data
------------+---------+---------+-------+------+-------------
1 | (0,1) | 12 | f | f | 23 27 00 00
2 | (0,2) | 12 | f | f | 24 27 00 00
3 | (0,3) | 12 | f | f | 25 27 00 00
4 | (0,4) | 12 | f | f | 26 27 00 00
5 | (0,5) | 12 | f | f | 27 27 00 00
6 | (0,6) | 12 | f | f | 28 27 00 00
7 | (0,7) | 12 | f | f | 29 27 00 00
8 | (0,8) | 12 | f | f | 2a 27 00 00
</screen>
All the other details are the same as explained in the previous item.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>

Expand Down
1 change: 1 addition & 0 deletions src/include/access/nbtree.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ typedef struct BTMetaPageData
#define P_ISLEAF(opaque) ((opaque)->btpo_flags & BTP_LEAF)
#define P_ISROOT(opaque) ((opaque)->btpo_flags & BTP_ROOT)
#define P_ISDELETED(opaque) ((opaque)->btpo_flags & BTP_DELETED)
#define P_ISMETA(opaque) ((opaque)->btpo_flags & BTP_META)
#define P_ISHALFDEAD(opaque) ((opaque)->btpo_flags & BTP_HALF_DEAD)
#define P_IGNORE(opaque) ((opaque)->btpo_flags & (BTP_DELETED|BTP_HALF_DEAD))
#define P_HAS_GARBAGE(opaque) ((opaque)->btpo_flags & BTP_HAS_GARBAGE)
Expand Down

0 comments on commit 193f5f9

Please sign in to comment.