Code review for btreefuncs additions: restrict to superusers to avoid
exposing user data to others, and clean up usage of deprecated APIs.
This commit is contained in:
parent
67bf7b919e
commit
08fc73c4c3
@ -1,5 +1,6 @@
|
|||||||
The functions in this module allow you to inspect the contents of data pages
|
The functions in this module allow you to inspect the contents of data pages
|
||||||
at a low level, for debugging purposes.
|
at a low level, for debugging purposes. All of these functions may be used
|
||||||
|
only by superusers.
|
||||||
|
|
||||||
1. Installation
|
1. Installation
|
||||||
|
|
||||||
@ -13,12 +14,12 @@ at a low level, for debugging purposes.
|
|||||||
------------
|
------------
|
||||||
get_raw_page reads one block of the named table and returns a copy as a
|
get_raw_page reads one block of the named table and returns a copy as a
|
||||||
bytea field. This allows a single time-consistent copy of the block to be
|
bytea field. This allows a single time-consistent copy of the block to be
|
||||||
made. Use of this functions is restricted to superusers.
|
made.
|
||||||
|
|
||||||
page_header
|
page_header
|
||||||
-----------
|
-----------
|
||||||
page_header shows fields which are common to all PostgreSQL heap and index
|
page_header shows fields which are common to all PostgreSQL heap and index
|
||||||
pages. Use of this function is restricted to superusers.
|
pages.
|
||||||
|
|
||||||
A page image obtained with get_raw_page should be passed as argument:
|
A page image obtained with get_raw_page should be passed as argument:
|
||||||
|
|
||||||
@ -36,8 +37,7 @@ at a low level, for debugging purposes.
|
|||||||
heap_page_items shows all line pointers on a heap page. For those line
|
heap_page_items shows all line pointers on a heap page. For those line
|
||||||
pointers that are in use, tuple headers are also shown. All tuples are
|
pointers that are in use, tuple headers are also shown. All tuples are
|
||||||
shown, whether or not the tuples were visible to an MVCC snapshot at the
|
shown, whether or not the tuples were visible to an MVCC snapshot at the
|
||||||
time the raw page was copied. Use of this function is restricted to
|
time the raw page was copied.
|
||||||
superusers.
|
|
||||||
|
|
||||||
A heap page image obtained with get_raw_page should be passed as argument:
|
A heap page image obtained with get_raw_page should be passed as argument:
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ at a low level, for debugging purposes.
|
|||||||
|
|
||||||
bt_metap
|
bt_metap
|
||||||
--------
|
--------
|
||||||
bt_metap() returns information about the btree index metapage:
|
bt_metap() returns information about a btree index's metapage:
|
||||||
|
|
||||||
test=> SELECT * FROM bt_metap('pg_cast_oid_index');
|
test=> SELECT * FROM bt_metap('pg_cast_oid_index');
|
||||||
-[ RECORD 1 ]-----
|
-[ RECORD 1 ]-----
|
||||||
|
@ -24,36 +24,24 @@
|
|||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "fmgr.h"
|
|
||||||
#include "funcapi.h"
|
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
#include "access/itup.h"
|
|
||||||
#include "access/nbtree.h"
|
#include "access/nbtree.h"
|
||||||
#include "access/transam.h"
|
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
|
#include "funcapi.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/inval.h"
|
|
||||||
|
|
||||||
PG_FUNCTION_INFO_V1(bt_metap);
|
|
||||||
PG_FUNCTION_INFO_V1(bt_page_items);
|
|
||||||
PG_FUNCTION_INFO_V1(bt_page_stats);
|
|
||||||
|
|
||||||
extern Datum bt_metap(PG_FUNCTION_ARGS);
|
extern Datum bt_metap(PG_FUNCTION_ARGS);
|
||||||
extern Datum bt_page_items(PG_FUNCTION_ARGS);
|
extern Datum bt_page_items(PG_FUNCTION_ARGS);
|
||||||
extern Datum bt_page_stats(PG_FUNCTION_ARGS);
|
extern Datum bt_page_stats(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
#define BTMETAP_TYPE "public.bt_metap_type"
|
PG_FUNCTION_INFO_V1(bt_metap);
|
||||||
#define BTMETAP_NCOLUMNS 6
|
PG_FUNCTION_INFO_V1(bt_page_items);
|
||||||
|
PG_FUNCTION_INFO_V1(bt_page_stats);
|
||||||
|
|
||||||
#define BTPAGEITEMS_TYPE "public.bt_page_items_type"
|
#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
|
||||||
#define BTPAGEITEMS_NCOLUMNS 6
|
|
||||||
|
|
||||||
#define BTPAGESTATS_TYPE "public.bt_page_stats_type"
|
|
||||||
#define BTPAGESTATS_NCOLUMNS 11
|
|
||||||
|
|
||||||
|
|
||||||
#define IS_INDEX(r) ((r)->rd_rel->relkind == 'i')
|
|
||||||
#define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
|
#define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
|
||||||
|
|
||||||
#define CHECK_PAGE_OFFSET_RANGE(pg, offnum) { \
|
#define CHECK_PAGE_OFFSET_RANGE(pg, offnum) { \
|
||||||
@ -93,42 +81,11 @@ typedef struct BTPageStat
|
|||||||
BTCycleId btpo_cycleid;
|
BTCycleId btpo_cycleid;
|
||||||
} BTPageStat;
|
} BTPageStat;
|
||||||
|
|
||||||
/* ------------------------------------------------
|
|
||||||
* A structure for a whole btree index statistics
|
|
||||||
* used by pgstatindex().
|
|
||||||
* ------------------------------------------------
|
|
||||||
*/
|
|
||||||
typedef struct BTIndexStat
|
|
||||||
{
|
|
||||||
uint32 magic;
|
|
||||||
uint32 version;
|
|
||||||
BlockNumber root_blkno;
|
|
||||||
uint32 level;
|
|
||||||
|
|
||||||
BlockNumber fastroot;
|
|
||||||
uint32 fastlevel;
|
|
||||||
|
|
||||||
uint32 live_items;
|
|
||||||
uint32 dead_items;
|
|
||||||
|
|
||||||
uint32 root_pages;
|
|
||||||
uint32 internal_pages;
|
|
||||||
uint32 leaf_pages;
|
|
||||||
uint32 empty_pages;
|
|
||||||
uint32 deleted_pages;
|
|
||||||
|
|
||||||
uint32 page_size;
|
|
||||||
uint32 avg_item_size;
|
|
||||||
|
|
||||||
uint32 max_avail;
|
|
||||||
uint32 free_space;
|
|
||||||
} BTIndexStat;
|
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------------
|
/* -------------------------------------------------
|
||||||
* GetBTPageStatistics()
|
* GetBTPageStatistics()
|
||||||
*
|
*
|
||||||
* Collect statistics of single b-tree leaf page
|
* Collect statistics of single b-tree page
|
||||||
* -------------------------------------------------
|
* -------------------------------------------------
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
@ -199,7 +156,7 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat * stat)
|
|||||||
/* -----------------------------------------------
|
/* -----------------------------------------------
|
||||||
* bt_page()
|
* bt_page()
|
||||||
*
|
*
|
||||||
* Usage: SELECT * FROM bt_page('t1_pkey', 0);
|
* Usage: SELECT * FROM bt_page('t1_pkey', 1);
|
||||||
* -----------------------------------------------
|
* -----------------------------------------------
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
@ -208,31 +165,33 @@ bt_page_stats(PG_FUNCTION_ARGS)
|
|||||||
text *relname = PG_GETARG_TEXT_P(0);
|
text *relname = PG_GETARG_TEXT_P(0);
|
||||||
uint32 blkno = PG_GETARG_UINT32(1);
|
uint32 blkno = PG_GETARG_UINT32(1);
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
Relation rel;
|
Relation rel;
|
||||||
RangeVar *relrv;
|
RangeVar *relrv;
|
||||||
Datum result;
|
Datum result;
|
||||||
|
HeapTuple tuple;
|
||||||
|
TupleDesc tupleDesc;
|
||||||
|
int j;
|
||||||
|
char *values[11];
|
||||||
|
BTPageStat stat;
|
||||||
|
|
||||||
|
if (!superuser())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
(errmsg("must be superuser to use pageinspect functions"))));
|
||||||
|
|
||||||
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
|
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
|
||||||
rel = relation_openrv(relrv, AccessShareLock);
|
rel = relation_openrv(relrv, AccessShareLock);
|
||||||
|
|
||||||
CHECK_RELATION_BLOCK_RANGE(rel, blkno);
|
|
||||||
|
|
||||||
buffer = ReadBuffer(rel, blkno);
|
|
||||||
|
|
||||||
if (!IS_INDEX(rel) || !IS_BTREE(rel))
|
if (!IS_INDEX(rel) || !IS_BTREE(rel))
|
||||||
elog(ERROR, "bt_page_stats() can only be used on b-tree index");
|
elog(ERROR, "relation \"%s\" is not a btree index",
|
||||||
|
RelationGetRelationName(rel));
|
||||||
|
|
||||||
if (blkno == 0)
|
if (blkno == 0)
|
||||||
elog(ERROR, "block 0 is a meta page");
|
elog(ERROR, "block 0 is a meta page");
|
||||||
|
|
||||||
{
|
CHECK_RELATION_BLOCK_RANGE(rel, blkno);
|
||||||
HeapTuple tuple;
|
|
||||||
TupleDesc tupleDesc;
|
|
||||||
int j;
|
|
||||||
char *values[BTPAGESTATS_NCOLUMNS];
|
|
||||||
|
|
||||||
BTPageStat stat;
|
buffer = ReadBuffer(rel, blkno);
|
||||||
|
|
||||||
/* keep compiler quiet */
|
/* keep compiler quiet */
|
||||||
stat.btpo_prev = stat.btpo_next = InvalidBlockNumber;
|
stat.btpo_prev = stat.btpo_next = InvalidBlockNumber;
|
||||||
@ -240,12 +199,13 @@ bt_page_stats(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
GetBTPageStatistics(blkno, buffer, &stat);
|
GetBTPageStatistics(blkno, buffer, &stat);
|
||||||
|
|
||||||
tupleDesc = RelationNameGetTupleDesc(BTPAGESTATS_TYPE);
|
/* 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");
|
||||||
|
|
||||||
j = 0;
|
j = 0;
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
snprintf(values[j++], 32, "%d", stat.blkno);
|
snprintf(values[j++], 32, "%d", stat.blkno);
|
||||||
|
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
snprintf(values[j++], 32, "%c", stat.type);
|
snprintf(values[j++], 32, "%c", stat.type);
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
@ -262,21 +222,18 @@ bt_page_stats(PG_FUNCTION_ARGS)
|
|||||||
snprintf(values[j++], 32, "%d", stat.btpo_prev);
|
snprintf(values[j++], 32, "%d", stat.btpo_prev);
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
snprintf(values[j++], 32, "%d", stat.btpo_next);
|
snprintf(values[j++], 32, "%d", stat.btpo_next);
|
||||||
|
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
if (stat.type == 'd')
|
if (stat.type == 'd')
|
||||||
snprintf(values[j++], 32, "%d", stat.btpo.xact);
|
snprintf(values[j++], 32, "%d", stat.btpo.xact);
|
||||||
else
|
else
|
||||||
snprintf(values[j++], 32, "%d", stat.btpo.level);
|
snprintf(values[j++], 32, "%d", stat.btpo.level);
|
||||||
|
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
snprintf(values[j++], 32, "%d", stat.btpo_flags);
|
snprintf(values[j++], 32, "%d", stat.btpo_flags);
|
||||||
|
|
||||||
tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
|
tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
|
||||||
values);
|
values);
|
||||||
|
|
||||||
result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple);
|
result = HeapTupleGetDatum(tuple);
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseBuffer(buffer);
|
ReleaseBuffer(buffer);
|
||||||
|
|
||||||
@ -288,22 +245,19 @@ bt_page_stats(PG_FUNCTION_ARGS)
|
|||||||
/*-------------------------------------------------------
|
/*-------------------------------------------------------
|
||||||
* bt_page_items()
|
* bt_page_items()
|
||||||
*
|
*
|
||||||
* Get IndexTupleData set in a leaf page
|
* Get IndexTupleData set in a btree page
|
||||||
*
|
*
|
||||||
* Usage: SELECT * FROM bt_page_items('t1_pkey', 0);
|
* Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
|
||||||
*-------------------------------------------------------
|
*-------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
/* ---------------------------------------------------
|
|
||||||
* data structure for SRF to hold a scan information
|
/*
|
||||||
* ---------------------------------------------------
|
* cross-call data structure for SRF
|
||||||
*/
|
*/
|
||||||
struct user_args
|
struct user_args
|
||||||
{
|
{
|
||||||
TupleDesc tupd;
|
|
||||||
Relation rel;
|
|
||||||
Buffer buffer;
|
|
||||||
Page page;
|
Page page;
|
||||||
uint16 offset;
|
OffsetNumber offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
@ -311,49 +265,72 @@ bt_page_items(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
text *relname = PG_GETARG_TEXT_P(0);
|
text *relname = PG_GETARG_TEXT_P(0);
|
||||||
uint32 blkno = PG_GETARG_UINT32(1);
|
uint32 blkno = PG_GETARG_UINT32(1);
|
||||||
|
|
||||||
RangeVar *relrv;
|
|
||||||
Datum result;
|
Datum result;
|
||||||
char *values[BTPAGEITEMS_NCOLUMNS];
|
char *values[6];
|
||||||
BTPageOpaque opaque;
|
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
ItemId id;
|
|
||||||
|
|
||||||
FuncCallContext *fctx;
|
FuncCallContext *fctx;
|
||||||
MemoryContext mctx;
|
MemoryContext mctx;
|
||||||
struct user_args *uargs = NULL;
|
struct user_args *uargs;
|
||||||
|
|
||||||
|
if (!superuser())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
(errmsg("must be superuser to use pageinspect functions"))));
|
||||||
|
|
||||||
|
if (SRF_IS_FIRSTCALL())
|
||||||
|
{
|
||||||
|
RangeVar *relrv;
|
||||||
|
Relation rel;
|
||||||
|
Buffer buffer;
|
||||||
|
BTPageOpaque opaque;
|
||||||
|
TupleDesc tupleDesc;
|
||||||
|
|
||||||
|
fctx = SRF_FIRSTCALL_INIT();
|
||||||
|
|
||||||
|
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
|
||||||
|
rel = relation_openrv(relrv, AccessShareLock);
|
||||||
|
|
||||||
|
if (!IS_INDEX(rel) || !IS_BTREE(rel))
|
||||||
|
elog(ERROR, "relation \"%s\" is not a btree index",
|
||||||
|
RelationGetRelationName(rel));
|
||||||
|
|
||||||
if (blkno == 0)
|
if (blkno == 0)
|
||||||
elog(ERROR, "block 0 is a meta page");
|
elog(ERROR, "block 0 is a meta page");
|
||||||
|
|
||||||
if (SRF_IS_FIRSTCALL())
|
CHECK_RELATION_BLOCK_RANGE(rel, blkno);
|
||||||
{
|
|
||||||
fctx = SRF_FIRSTCALL_INIT();
|
buffer = ReadBuffer(rel, blkno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We copy the page into local storage to avoid holding pin on
|
||||||
|
* the buffer longer than we must, and possibly failing to
|
||||||
|
* release it at all if the calling query doesn't fetch all rows.
|
||||||
|
*/
|
||||||
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
|
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
|
||||||
|
|
||||||
uargs = palloc(sizeof(struct user_args));
|
uargs = palloc(sizeof(struct user_args));
|
||||||
|
|
||||||
uargs->tupd = RelationNameGetTupleDesc(BTPAGEITEMS_TYPE);
|
uargs->page = palloc(BLCKSZ);
|
||||||
|
memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ);
|
||||||
|
|
||||||
|
ReleaseBuffer(buffer);
|
||||||
|
relation_close(rel, AccessShareLock);
|
||||||
|
|
||||||
uargs->offset = FirstOffsetNumber;
|
uargs->offset = FirstOffsetNumber;
|
||||||
|
|
||||||
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
|
|
||||||
uargs->rel = relation_openrv(relrv, AccessShareLock);
|
|
||||||
|
|
||||||
CHECK_RELATION_BLOCK_RANGE(uargs->rel, blkno);
|
|
||||||
|
|
||||||
uargs->buffer = ReadBuffer(uargs->rel, blkno);
|
|
||||||
|
|
||||||
if (!IS_INDEX(uargs->rel) || !IS_BTREE(uargs->rel))
|
|
||||||
elog(ERROR, "bt_page_items() can only be used on b-tree index");
|
|
||||||
|
|
||||||
uargs->page = BufferGetPage(uargs->buffer);
|
|
||||||
|
|
||||||
opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
|
opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
|
||||||
|
|
||||||
if (P_ISDELETED(opaque))
|
if (P_ISDELETED(opaque))
|
||||||
elog(NOTICE, "page is deleted");
|
elog(NOTICE, "page is deleted");
|
||||||
|
|
||||||
fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
|
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;
|
fctx->user_fctx = uargs;
|
||||||
|
|
||||||
MemoryContextSwitchTo(mctx);
|
MemoryContextSwitchTo(mctx);
|
||||||
@ -364,7 +341,13 @@ bt_page_items(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
if (fctx->call_cntr < fctx->max_calls)
|
if (fctx->call_cntr < fctx->max_calls)
|
||||||
{
|
{
|
||||||
|
ItemId id;
|
||||||
IndexTuple itup;
|
IndexTuple itup;
|
||||||
|
int j;
|
||||||
|
int off;
|
||||||
|
int dlen;
|
||||||
|
char *dump;
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
id = PageGetItemId(uargs->page, uargs->offset);
|
id = PageGetItemId(uargs->page, uargs->offset);
|
||||||
|
|
||||||
@ -373,15 +356,13 @@ bt_page_items(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
itup = (IndexTuple) PageGetItem(uargs->page, id);
|
itup = (IndexTuple) PageGetItem(uargs->page, id);
|
||||||
|
|
||||||
{
|
j = 0;
|
||||||
int j = 0;
|
|
||||||
|
|
||||||
BlockNumber blkno = BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid));
|
|
||||||
|
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
snprintf(values[j++], 32, "%d", uargs->offset);
|
snprintf(values[j++], 32, "%d", uargs->offset);
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
snprintf(values[j++], 32, "(%u,%u)", blkno, itup->t_tid.ip_posid);
|
snprintf(values[j++], 32, "(%u,%u)",
|
||||||
|
BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)),
|
||||||
|
itup->t_tid.ip_posid);
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup));
|
snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup));
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
@ -389,34 +370,20 @@ bt_page_items(PG_FUNCTION_ARGS)
|
|||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
snprintf(values[j++], 32, "%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');
|
snprintf(values[j++], 32, "%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');
|
||||||
|
|
||||||
{
|
ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
|
||||||
int off;
|
dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
|
||||||
char *dump;
|
dump = palloc0(dlen * 3 + 1);
|
||||||
char *ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
|
|
||||||
|
|
||||||
dump = palloc(IndexTupleSize(itup) * 3);
|
|
||||||
memset(dump, 0, IndexTupleSize(itup) * 3);
|
|
||||||
|
|
||||||
for (off = 0;
|
|
||||||
off < IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
|
|
||||||
off++)
|
|
||||||
{
|
|
||||||
if (dump[0] == '\0')
|
|
||||||
sprintf(dump, "%02x", *(ptr + off) & 0xff);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char buf[4];
|
|
||||||
|
|
||||||
sprintf(buf, " %02x", *(ptr + off) & 0xff);
|
|
||||||
strcat(dump, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
values[j] = dump;
|
values[j] = dump;
|
||||||
|
for (off = 0; off < dlen; off++)
|
||||||
|
{
|
||||||
|
if (off > 0)
|
||||||
|
*dump++ = ' ';
|
||||||
|
sprintf(dump, "%02x", *(ptr + off) & 0xff);
|
||||||
|
dump += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(uargs->tupd), values);
|
tuple = BuildTupleFromCStrings(fctx->attinmeta, values);
|
||||||
result = TupleGetDatum(TupleDescGetSlot(uargs->tupd), tuple);
|
result = HeapTupleGetDatum(tuple);
|
||||||
}
|
|
||||||
|
|
||||||
uargs->offset = uargs->offset + 1;
|
uargs->offset = uargs->offset + 1;
|
||||||
|
|
||||||
@ -424,9 +391,8 @@ bt_page_items(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReleaseBuffer(uargs->buffer);
|
pfree(uargs->page);
|
||||||
relation_close(uargs->rel, AccessShareLock);
|
pfree(uargs);
|
||||||
|
|
||||||
SRF_RETURN_DONE(fctx);
|
SRF_RETURN_DONE(fctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -435,7 +401,7 @@ bt_page_items(PG_FUNCTION_ARGS)
|
|||||||
/* ------------------------------------------------
|
/* ------------------------------------------------
|
||||||
* bt_metap()
|
* bt_metap()
|
||||||
*
|
*
|
||||||
* Get a btree meta-page information
|
* Get a btree's meta-page information
|
||||||
*
|
*
|
||||||
* Usage: SELECT * FROM bt_metap('t1_pkey')
|
* Usage: SELECT * FROM bt_metap('t1_pkey')
|
||||||
* ------------------------------------------------
|
* ------------------------------------------------
|
||||||
@ -444,33 +410,36 @@ Datum
|
|||||||
bt_metap(PG_FUNCTION_ARGS)
|
bt_metap(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *relname = PG_GETARG_TEXT_P(0);
|
text *relname = PG_GETARG_TEXT_P(0);
|
||||||
Buffer buffer;
|
Datum result;
|
||||||
|
|
||||||
Relation rel;
|
Relation rel;
|
||||||
RangeVar *relrv;
|
RangeVar *relrv;
|
||||||
Datum result;
|
BTMetaPageData *metad;
|
||||||
|
TupleDesc tupleDesc;
|
||||||
|
int j;
|
||||||
|
char *values[6];
|
||||||
|
Buffer buffer;
|
||||||
|
Page page;
|
||||||
|
HeapTuple tuple;
|
||||||
|
|
||||||
|
if (!superuser())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
(errmsg("must be superuser to use pageinspect functions"))));
|
||||||
|
|
||||||
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
|
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
|
||||||
rel = relation_openrv(relrv, AccessShareLock);
|
rel = relation_openrv(relrv, AccessShareLock);
|
||||||
|
|
||||||
if (!IS_INDEX(rel) || !IS_BTREE(rel))
|
if (!IS_INDEX(rel) || !IS_BTREE(rel))
|
||||||
elog(ERROR, "bt_metap() can only be used on b-tree index");
|
elog(ERROR, "relation \"%s\" is not a btree index",
|
||||||
|
RelationGetRelationName(rel));
|
||||||
|
|
||||||
buffer = ReadBuffer(rel, 0);
|
buffer = ReadBuffer(rel, 0);
|
||||||
|
page = BufferGetPage(buffer);
|
||||||
{
|
|
||||||
BTMetaPageData *metad;
|
|
||||||
|
|
||||||
TupleDesc tupleDesc;
|
|
||||||
int j;
|
|
||||||
char *values[BTMETAP_NCOLUMNS];
|
|
||||||
HeapTuple tuple;
|
|
||||||
|
|
||||||
Page page = BufferGetPage(buffer);
|
|
||||||
|
|
||||||
metad = BTPageGetMeta(page);
|
metad = BTPageGetMeta(page);
|
||||||
|
|
||||||
tupleDesc = RelationNameGetTupleDesc(BTMETAP_TYPE);
|
/* 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");
|
||||||
|
|
||||||
j = 0;
|
j = 0;
|
||||||
values[j] = palloc(32);
|
values[j] = palloc(32);
|
||||||
@ -489,8 +458,7 @@ bt_metap(PG_FUNCTION_ARGS)
|
|||||||
tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
|
tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
|
||||||
values);
|
values);
|
||||||
|
|
||||||
result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple);
|
result = HeapTupleGetDatum(tuple);
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseBuffer(buffer);
|
ReleaseBuffer(buffer);
|
||||||
|
|
||||||
|
@ -12,98 +12,80 @@ LANGUAGE C STRICT;
|
|||||||
--
|
--
|
||||||
-- page_header()
|
-- page_header()
|
||||||
--
|
--
|
||||||
CREATE TYPE page_header_type AS (
|
CREATE OR REPLACE FUNCTION page_header(IN page bytea,
|
||||||
lsn text,
|
OUT lsn text,
|
||||||
tli smallint,
|
OUT tli smallint,
|
||||||
flags smallint,
|
OUT flags smallint,
|
||||||
lower smallint,
|
OUT lower smallint,
|
||||||
upper smallint,
|
OUT upper smallint,
|
||||||
special smallint,
|
OUT special smallint,
|
||||||
pagesize smallint,
|
OUT pagesize smallint,
|
||||||
version smallint
|
OUT version smallint)
|
||||||
);
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION page_header(bytea)
|
|
||||||
RETURNS page_header_type
|
|
||||||
AS 'MODULE_PATHNAME', 'page_header'
|
AS 'MODULE_PATHNAME', 'page_header'
|
||||||
LANGUAGE C STRICT;
|
LANGUAGE C STRICT;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- heap_page_items()
|
-- heap_page_items()
|
||||||
--
|
--
|
||||||
CREATE TYPE heap_page_items_type AS (
|
CREATE OR REPLACE FUNCTION heap_page_items(IN page bytea,
|
||||||
lp smallint,
|
OUT lp smallint,
|
||||||
lp_off smallint,
|
OUT lp_off smallint,
|
||||||
lp_flags smallint,
|
OUT lp_flags smallint,
|
||||||
lp_len smallint,
|
OUT lp_len smallint,
|
||||||
t_xmin xid,
|
OUT t_xmin xid,
|
||||||
t_xmax xid,
|
OUT t_xmax xid,
|
||||||
t_field3 int4,
|
OUT t_field3 int4,
|
||||||
t_ctid tid,
|
OUT t_ctid tid,
|
||||||
t_infomask2 smallint,
|
OUT t_infomask2 smallint,
|
||||||
t_infomask smallint,
|
OUT t_infomask smallint,
|
||||||
t_hoff smallint,
|
OUT t_hoff smallint,
|
||||||
t_bits text,
|
OUT t_bits text,
|
||||||
t_oid oid
|
OUT t_oid oid)
|
||||||
);
|
RETURNS SETOF record
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION heap_page_items(bytea)
|
|
||||||
RETURNS SETOF heap_page_items_type
|
|
||||||
AS 'MODULE_PATHNAME', 'heap_page_items'
|
AS 'MODULE_PATHNAME', 'heap_page_items'
|
||||||
LANGUAGE C STRICT;
|
LANGUAGE C STRICT;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- bt_metap()
|
-- bt_metap()
|
||||||
--
|
--
|
||||||
CREATE TYPE bt_metap_type AS (
|
CREATE OR REPLACE FUNCTION bt_metap(IN relname text,
|
||||||
magic int4,
|
OUT magic int4,
|
||||||
version int4,
|
OUT version int4,
|
||||||
root int4,
|
OUT root int4,
|
||||||
level int4,
|
OUT level int4,
|
||||||
fastroot int4,
|
OUT fastroot int4,
|
||||||
fastlevel int4
|
OUT fastlevel int4)
|
||||||
);
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION bt_metap(text)
|
|
||||||
RETURNS bt_metap_type
|
|
||||||
AS 'MODULE_PATHNAME', 'bt_metap'
|
AS 'MODULE_PATHNAME', 'bt_metap'
|
||||||
LANGUAGE 'C' STRICT;
|
LANGUAGE C STRICT;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- bt_page_stats()
|
-- bt_page_stats()
|
||||||
--
|
--
|
||||||
CREATE TYPE bt_page_stats_type AS (
|
CREATE OR REPLACE FUNCTION bt_page_stats(IN relname text, IN blkno int4,
|
||||||
blkno int4,
|
OUT blkno int4,
|
||||||
type char,
|
OUT type "char",
|
||||||
live_items int4,
|
OUT live_items int4,
|
||||||
dead_items int4,
|
OUT dead_items int4,
|
||||||
avg_item_size float,
|
OUT avg_item_size int4,
|
||||||
page_size int4,
|
OUT page_size int4,
|
||||||
free_size int4,
|
OUT free_size int4,
|
||||||
btpo_prev int4,
|
OUT btpo_prev int4,
|
||||||
btpo_next int4,
|
OUT btpo_next int4,
|
||||||
btpo int4,
|
OUT btpo int4,
|
||||||
btpo_flags int4
|
OUT btpo_flags int4)
|
||||||
);
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION bt_page_stats(text, int4)
|
|
||||||
RETURNS bt_page_stats_type
|
|
||||||
AS 'MODULE_PATHNAME', 'bt_page_stats'
|
AS 'MODULE_PATHNAME', 'bt_page_stats'
|
||||||
LANGUAGE 'C' STRICT;
|
LANGUAGE C STRICT;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- bt_page_items()
|
-- bt_page_items()
|
||||||
--
|
--
|
||||||
CREATE TYPE bt_page_items_type AS (
|
CREATE OR REPLACE FUNCTION bt_page_items(IN relname text, IN blkno int4,
|
||||||
itemoffset smallint,
|
OUT itemoffset smallint,
|
||||||
ctid tid,
|
OUT ctid tid,
|
||||||
itemlen smallint,
|
OUT itemlen smallint,
|
||||||
nulls bool,
|
OUT nulls bool,
|
||||||
vars bool,
|
OUT vars bool,
|
||||||
data text
|
OUT data text)
|
||||||
);
|
RETURNS SETOF record
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION bt_page_items(text, int4)
|
|
||||||
RETURNS SETOF bt_page_items_type
|
|
||||||
AS 'MODULE_PATHNAME', 'bt_page_items'
|
AS 'MODULE_PATHNAME', 'bt_page_items'
|
||||||
LANGUAGE 'C' STRICT;
|
LANGUAGE C STRICT;
|
||||||
|
@ -2,19 +2,8 @@
|
|||||||
SET search_path = public;
|
SET search_path = public;
|
||||||
|
|
||||||
DROP FUNCTION get_raw_page(text, int4);
|
DROP FUNCTION get_raw_page(text, int4);
|
||||||
|
|
||||||
DROP FUNCTION page_header(bytea);
|
DROP FUNCTION page_header(bytea);
|
||||||
DROP TYPE page_header_type;
|
|
||||||
|
|
||||||
DROP FUNCTION heap_page_items(bytea);
|
DROP FUNCTION heap_page_items(bytea);
|
||||||
DROP TYPE heap_page_items_type;
|
|
||||||
|
|
||||||
DROP FUNCTION bt_metap(text);
|
DROP FUNCTION bt_metap(text);
|
||||||
DROP TYPE bt_metap_type;
|
|
||||||
|
|
||||||
DROP FUNCTION bt_page_stats(text, int4);
|
DROP FUNCTION bt_page_stats(text, int4);
|
||||||
DROP TYPE bt_page_stats_type;
|
|
||||||
|
|
||||||
DROP FUNCTION bt_page_items(text, int4);
|
DROP FUNCTION bt_page_items(text, int4);
|
||||||
DROP TYPE bt_page_items_type;
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user