mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Add bt_multi_page_stats() function to contrib/pageinspect.
This is like the existing bt_page_stats() function, but it can report on a range of pages rather than just one at a time. I don't have a huge amount of faith in the portability of the new test cases, but they do pass in a 32-bit FreeBSD VM here. Further adjustment may be needed depending on buildfarm results. Hamid Akhtar, reviewed by Naeem Akhter, Bertrand Drouvot, Bharath Rupireddy, and myself Discussion: https://postgr.es/m/CANugjht-=oGMRmNJKMqnBC69y7vr+wHDmm0ZK6-1pJsxoBKBbA@mail.gmail.com
This commit is contained in:
@ -13,7 +13,7 @@ OBJS = \
|
|||||||
rawpage.o
|
rawpage.o
|
||||||
|
|
||||||
EXTENSION = pageinspect
|
EXTENSION = pageinspect
|
||||||
DATA = pageinspect--1.10--1.11.sql \
|
DATA = pageinspect--1.11--1.12.sql pageinspect--1.10--1.11.sql \
|
||||||
pageinspect--1.9--1.10.sql pageinspect--1.8--1.9.sql \
|
pageinspect--1.9--1.10.sql pageinspect--1.8--1.9.sql \
|
||||||
pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \
|
pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \
|
||||||
pageinspect--1.5.sql pageinspect--1.5--1.6.sql \
|
pageinspect--1.5.sql pageinspect--1.5--1.6.sql \
|
||||||
|
@ -46,17 +46,13 @@ PG_FUNCTION_INFO_V1(bt_page_items);
|
|||||||
PG_FUNCTION_INFO_V1(bt_page_items_bytea);
|
PG_FUNCTION_INFO_V1(bt_page_items_bytea);
|
||||||
PG_FUNCTION_INFO_V1(bt_page_stats_1_9);
|
PG_FUNCTION_INFO_V1(bt_page_stats_1_9);
|
||||||
PG_FUNCTION_INFO_V1(bt_page_stats);
|
PG_FUNCTION_INFO_V1(bt_page_stats);
|
||||||
|
PG_FUNCTION_INFO_V1(bt_multi_page_stats);
|
||||||
|
|
||||||
#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
|
#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
|
||||||
#define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
|
#define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
|
||||||
#define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X))
|
#define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X))
|
||||||
#define ItemPointerGetDatum(X) PointerGetDatum(X)
|
#define ItemPointerGetDatum(X) PointerGetDatum(X)
|
||||||
|
|
||||||
/* note: BlockNumber is unsigned, hence can't be negative */
|
|
||||||
#define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \
|
|
||||||
if ( RelationGetNumberOfBlocks(rel) <= (BlockNumber) (blkno) ) \
|
|
||||||
elog(ERROR, "block number out of range"); }
|
|
||||||
|
|
||||||
/* ------------------------------------------------
|
/* ------------------------------------------------
|
||||||
* structure for single btree page statistics
|
* structure for single btree page statistics
|
||||||
* ------------------------------------------------
|
* ------------------------------------------------
|
||||||
@ -80,6 +76,29 @@ typedef struct BTPageStat
|
|||||||
BTCycleId btpo_cycleid;
|
BTCycleId btpo_cycleid;
|
||||||
} BTPageStat;
|
} BTPageStat;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cross-call data structure for SRF for page stats
|
||||||
|
*/
|
||||||
|
typedef struct ua_page_stats
|
||||||
|
{
|
||||||
|
Oid relid;
|
||||||
|
int64 blkno;
|
||||||
|
int64 blk_count;
|
||||||
|
bool allpages;
|
||||||
|
} ua_page_stats;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cross-call data structure for SRF for page items
|
||||||
|
*/
|
||||||
|
typedef struct ua_page_items
|
||||||
|
{
|
||||||
|
Page page;
|
||||||
|
OffsetNumber offset;
|
||||||
|
bool leafpage;
|
||||||
|
bool rightmost;
|
||||||
|
TupleDesc tupd;
|
||||||
|
} ua_page_items;
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------------
|
/* -------------------------------------------------
|
||||||
* GetBTPageStatistics()
|
* GetBTPageStatistics()
|
||||||
@ -176,10 +195,68 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
|
|||||||
stat->avg_item_size = 0;
|
stat->avg_item_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------
|
||||||
|
* check_relation_block_range()
|
||||||
|
*
|
||||||
|
* Verify that a block number (given as int64) is valid for the relation.
|
||||||
|
* -----------------------------------------------
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
check_relation_block_range(Relation rel, int64 blkno)
|
||||||
|
{
|
||||||
|
/* Ensure we can cast to BlockNumber */
|
||||||
|
if (blkno < 0 || blkno > MaxBlockNumber)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("invalid block number %lld",
|
||||||
|
(long long) blkno)));
|
||||||
|
|
||||||
|
if ((BlockNumber) (blkno) >= RelationGetNumberOfBlocks(rel))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("block number %lld is out of range",
|
||||||
|
(long long) blkno)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------
|
||||||
|
* bt_index_block_validate()
|
||||||
|
*
|
||||||
|
* Validate index type is btree and block number
|
||||||
|
* is valid (and not the metapage).
|
||||||
|
* -----------------------------------------------
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
bt_index_block_validate(Relation rel, int64 blkno)
|
||||||
|
{
|
||||||
|
if (!IS_INDEX(rel) || !IS_BTREE(rel))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
|
errmsg("\"%s\" is not a %s index",
|
||||||
|
RelationGetRelationName(rel), "btree")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reject attempts to read non-local temporary relations; we would be
|
||||||
|
* likely to get wrong data since we have no visibility into the owning
|
||||||
|
* session's local buffers.
|
||||||
|
*/
|
||||||
|
if (RELATION_IS_OTHER_TEMP(rel))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot access temporary tables of other sessions")));
|
||||||
|
|
||||||
|
if (blkno == 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("block 0 is a meta page")));
|
||||||
|
|
||||||
|
check_relation_block_range(rel, blkno);
|
||||||
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------
|
/* -----------------------------------------------
|
||||||
* bt_page_stats()
|
* bt_page_stats()
|
||||||
*
|
*
|
||||||
* Usage: SELECT * FROM bt_page_stats('t1_pkey', 1);
|
* Usage: SELECT * FROM bt_page_stats('t1_pkey', 1);
|
||||||
|
* Arguments are index relation name and block number
|
||||||
* -----------------------------------------------
|
* -----------------------------------------------
|
||||||
*/
|
*/
|
||||||
static Datum
|
static Datum
|
||||||
@ -205,33 +282,7 @@ bt_page_stats_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
|
|||||||
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))
|
bt_index_block_validate(rel, blkno);
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
||||||
errmsg("\"%s\" is not a %s index",
|
|
||||||
RelationGetRelationName(rel), "btree")));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reject attempts to read non-local temporary relations; we would be
|
|
||||||
* likely to get wrong data since we have no visibility into the owning
|
|
||||||
* session's local buffers.
|
|
||||||
*/
|
|
||||||
if (RELATION_IS_OTHER_TEMP(rel))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("cannot access temporary tables of other sessions")));
|
|
||||||
|
|
||||||
if (blkno < 0 || blkno > MaxBlockNumber)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("invalid block number")));
|
|
||||||
|
|
||||||
if (blkno == 0)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("block 0 is a meta page")));
|
|
||||||
|
|
||||||
CHECK_RELATION_BLOCK_RANGE(rel, blkno);
|
|
||||||
|
|
||||||
buffer = ReadBuffer(rel, blkno);
|
buffer = ReadBuffer(rel, blkno);
|
||||||
LockBuffer(buffer, BUFFER_LOCK_SHARE);
|
LockBuffer(buffer, BUFFER_LOCK_SHARE);
|
||||||
@ -284,17 +335,144 @@ bt_page_stats(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/* -----------------------------------------------
|
||||||
* cross-call data structure for SRF
|
* bt_multi_page_stats()
|
||||||
|
*
|
||||||
|
* Usage: SELECT * FROM bt_page_stats('t1_pkey', 1, 2);
|
||||||
|
* Arguments are index relation name, first block number, number of blocks
|
||||||
|
* (but number of blocks can be negative to mean "read all the rest")
|
||||||
|
* -----------------------------------------------
|
||||||
*/
|
*/
|
||||||
struct user_args
|
Datum
|
||||||
|
bt_multi_page_stats(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Page page;
|
Relation rel;
|
||||||
OffsetNumber offset;
|
ua_page_stats *uargs;
|
||||||
bool leafpage;
|
FuncCallContext *fctx;
|
||||||
bool rightmost;
|
MemoryContext mctx;
|
||||||
TupleDesc tupd;
|
|
||||||
};
|
if (!superuser())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("must be superuser to use pageinspect functions")));
|
||||||
|
|
||||||
|
if (SRF_IS_FIRSTCALL())
|
||||||
|
{
|
||||||
|
text *relname = PG_GETARG_TEXT_PP(0);
|
||||||
|
int64 blkno = PG_GETARG_INT64(1);
|
||||||
|
int64 blk_count = PG_GETARG_INT64(2);
|
||||||
|
RangeVar *relrv;
|
||||||
|
|
||||||
|
fctx = SRF_FIRSTCALL_INIT();
|
||||||
|
|
||||||
|
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
|
||||||
|
rel = relation_openrv(relrv, AccessShareLock);
|
||||||
|
|
||||||
|
/* Check that rel is a valid btree index and 1st block number is OK */
|
||||||
|
bt_index_block_validate(rel, blkno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if upper bound of the specified range is valid. If only one
|
||||||
|
* page is requested, skip as we've already validated the page. (Also,
|
||||||
|
* it's important to skip this if blk_count is negative.)
|
||||||
|
*/
|
||||||
|
if (blk_count > 1)
|
||||||
|
check_relation_block_range(rel, blkno + blk_count - 1);
|
||||||
|
|
||||||
|
/* Save arguments for reuse */
|
||||||
|
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
|
||||||
|
|
||||||
|
uargs = palloc(sizeof(ua_page_stats));
|
||||||
|
|
||||||
|
uargs->relid = RelationGetRelid(rel);
|
||||||
|
uargs->blkno = blkno;
|
||||||
|
uargs->blk_count = blk_count;
|
||||||
|
uargs->allpages = (blk_count < 0);
|
||||||
|
|
||||||
|
fctx->user_fctx = uargs;
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(mctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To avoid possibly leaking a relcache reference if the SRF isn't run
|
||||||
|
* to completion, we close and re-open the index rel each time
|
||||||
|
* through, using the index's OID for re-opens to ensure we get the
|
||||||
|
* same rel. Keep the AccessShareLock though, to ensure it doesn't go
|
||||||
|
* away underneath us.
|
||||||
|
*/
|
||||||
|
relation_close(rel, NoLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
fctx = SRF_PERCALL_SETUP();
|
||||||
|
uargs = fctx->user_fctx;
|
||||||
|
|
||||||
|
/* We should have lock already */
|
||||||
|
rel = relation_open(uargs->relid, NoLock);
|
||||||
|
|
||||||
|
/* In all-pages mode, recheck the index length each time */
|
||||||
|
if (uargs->allpages)
|
||||||
|
uargs->blk_count = RelationGetNumberOfBlocks(rel) - uargs->blkno;
|
||||||
|
|
||||||
|
if (uargs->blk_count > 0)
|
||||||
|
{
|
||||||
|
/* We need to fetch next block statistics */
|
||||||
|
Buffer buffer;
|
||||||
|
Datum result;
|
||||||
|
HeapTuple tuple;
|
||||||
|
int j;
|
||||||
|
char *values[11];
|
||||||
|
BTPageStat stat;
|
||||||
|
TupleDesc tupleDesc;
|
||||||
|
|
||||||
|
buffer = ReadBuffer(rel, uargs->blkno);
|
||||||
|
LockBuffer(buffer, BUFFER_LOCK_SHARE);
|
||||||
|
|
||||||
|
/* keep compiler quiet */
|
||||||
|
stat.btpo_prev = stat.btpo_next = InvalidBlockNumber;
|
||||||
|
stat.btpo_flags = stat.free_size = stat.avg_item_size = 0;
|
||||||
|
|
||||||
|
GetBTPageStatistics(uargs->blkno, buffer, &stat);
|
||||||
|
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
relation_close(rel, NoLock);
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
values[j++] = psprintf("%u", stat.blkno);
|
||||||
|
values[j++] = psprintf("%c", stat.type);
|
||||||
|
values[j++] = psprintf("%u", stat.live_items);
|
||||||
|
values[j++] = psprintf("%u", stat.dead_items);
|
||||||
|
values[j++] = psprintf("%u", stat.avg_item_size);
|
||||||
|
values[j++] = psprintf("%u", stat.page_size);
|
||||||
|
values[j++] = psprintf("%u", stat.free_size);
|
||||||
|
values[j++] = psprintf("%u", stat.btpo_prev);
|
||||||
|
values[j++] = psprintf("%u", stat.btpo_next);
|
||||||
|
values[j++] = psprintf("%u", stat.btpo_level);
|
||||||
|
values[j++] = psprintf("%d", stat.btpo_flags);
|
||||||
|
|
||||||
|
/* Construct tuple to be returned */
|
||||||
|
tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
|
||||||
|
values);
|
||||||
|
|
||||||
|
result = HeapTupleGetDatum(tuple);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move to the next block number and decrement the number of blocks
|
||||||
|
* still to be fetched
|
||||||
|
*/
|
||||||
|
uargs->blkno++;
|
||||||
|
uargs->blk_count--;
|
||||||
|
|
||||||
|
SRF_RETURN_NEXT(fctx, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Done, so finally we can release the index lock */
|
||||||
|
relation_close(rel, AccessShareLock);
|
||||||
|
SRF_RETURN_DONE(fctx);
|
||||||
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------
|
/*-------------------------------------------------------
|
||||||
* bt_page_print_tuples()
|
* bt_page_print_tuples()
|
||||||
@ -303,7 +481,7 @@ struct user_args
|
|||||||
* ------------------------------------------------------
|
* ------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
static Datum
|
static Datum
|
||||||
bt_page_print_tuples(struct user_args *uargs)
|
bt_page_print_tuples(ua_page_items *uargs)
|
||||||
{
|
{
|
||||||
Page page = uargs->page;
|
Page page = uargs->page;
|
||||||
OffsetNumber offset = uargs->offset;
|
OffsetNumber offset = uargs->offset;
|
||||||
@ -453,7 +631,7 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
|
|||||||
Datum result;
|
Datum result;
|
||||||
FuncCallContext *fctx;
|
FuncCallContext *fctx;
|
||||||
MemoryContext mctx;
|
MemoryContext mctx;
|
||||||
struct user_args *uargs;
|
ua_page_items *uargs;
|
||||||
|
|
||||||
if (!superuser())
|
if (!superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -473,33 +651,7 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
|
|||||||
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))
|
bt_index_block_validate(rel, blkno);
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
||||||
errmsg("\"%s\" is not a %s index",
|
|
||||||
RelationGetRelationName(rel), "btree")));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reject attempts to read non-local temporary relations; we would be
|
|
||||||
* likely to get wrong data since we have no visibility into the
|
|
||||||
* owning session's local buffers.
|
|
||||||
*/
|
|
||||||
if (RELATION_IS_OTHER_TEMP(rel))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("cannot access temporary tables of other sessions")));
|
|
||||||
|
|
||||||
if (blkno < 0 || blkno > MaxBlockNumber)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("invalid block number")));
|
|
||||||
|
|
||||||
if (blkno == 0)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("block 0 is a meta page")));
|
|
||||||
|
|
||||||
CHECK_RELATION_BLOCK_RANGE(rel, blkno);
|
|
||||||
|
|
||||||
buffer = ReadBuffer(rel, blkno);
|
buffer = ReadBuffer(rel, blkno);
|
||||||
LockBuffer(buffer, BUFFER_LOCK_SHARE);
|
LockBuffer(buffer, BUFFER_LOCK_SHARE);
|
||||||
@ -511,7 +663,7 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
|
|||||||
*/
|
*/
|
||||||
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
|
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
|
||||||
|
|
||||||
uargs = palloc(sizeof(struct user_args));
|
uargs = palloc(sizeof(ua_page_items));
|
||||||
|
|
||||||
uargs->page = palloc(BLCKSZ);
|
uargs->page = palloc(BLCKSZ);
|
||||||
memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ);
|
memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ);
|
||||||
@ -587,7 +739,7 @@ bt_page_items_bytea(PG_FUNCTION_ARGS)
|
|||||||
bytea *raw_page = PG_GETARG_BYTEA_P(0);
|
bytea *raw_page = PG_GETARG_BYTEA_P(0);
|
||||||
Datum result;
|
Datum result;
|
||||||
FuncCallContext *fctx;
|
FuncCallContext *fctx;
|
||||||
struct user_args *uargs;
|
ua_page_items *uargs;
|
||||||
|
|
||||||
if (!superuser())
|
if (!superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -603,7 +755,7 @@ bt_page_items_bytea(PG_FUNCTION_ARGS)
|
|||||||
fctx = SRF_FIRSTCALL_INIT();
|
fctx = SRF_FIRSTCALL_INIT();
|
||||||
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
|
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
|
||||||
|
|
||||||
uargs = palloc(sizeof(struct user_args));
|
uargs = palloc(sizeof(ua_page_items));
|
||||||
|
|
||||||
uargs->page = get_page_from_raw(raw_page);
|
uargs->page = get_page_from_raw(raw_page);
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ last_cleanup_num_tuples | -1
|
|||||||
allequalimage | t
|
allequalimage | t
|
||||||
|
|
||||||
SELECT * FROM bt_page_stats('test1_a_idx', -1);
|
SELECT * FROM bt_page_stats('test1_a_idx', -1);
|
||||||
ERROR: invalid block number
|
ERROR: invalid block number -1
|
||||||
SELECT * FROM bt_page_stats('test1_a_idx', 0);
|
SELECT * FROM bt_page_stats('test1_a_idx', 0);
|
||||||
ERROR: block 0 is a meta page
|
ERROR: block 0 is a meta page
|
||||||
SELECT * FROM bt_page_stats('test1_a_idx', 1);
|
SELECT * FROM bt_page_stats('test1_a_idx', 1);
|
||||||
@ -33,9 +33,122 @@ btpo_level | 0
|
|||||||
btpo_flags | 3
|
btpo_flags | 3
|
||||||
|
|
||||||
SELECT * FROM bt_page_stats('test1_a_idx', 2);
|
SELECT * FROM bt_page_stats('test1_a_idx', 2);
|
||||||
ERROR: block number out of range
|
ERROR: block number 2 is out of range
|
||||||
|
-- bt_multi_page_stats() function returns a set of records of page statistics.
|
||||||
|
CREATE TABLE test2 AS (SELECT generate_series(1, 1000)::int8 AS col1);
|
||||||
|
CREATE INDEX test2_col1_idx ON test2(col1);
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 0, 1);
|
||||||
|
ERROR: block 0 is a meta page
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 1, -1);
|
||||||
|
-[ RECORD 1 ]-+-----
|
||||||
|
blkno | 1
|
||||||
|
type | l
|
||||||
|
live_items | 367
|
||||||
|
dead_items | 0
|
||||||
|
avg_item_size | 16
|
||||||
|
page_size | 8192
|
||||||
|
free_size | 808
|
||||||
|
btpo_prev | 0
|
||||||
|
btpo_next | 2
|
||||||
|
btpo_level | 0
|
||||||
|
btpo_flags | 1
|
||||||
|
-[ RECORD 2 ]-+-----
|
||||||
|
blkno | 2
|
||||||
|
type | l
|
||||||
|
live_items | 367
|
||||||
|
dead_items | 0
|
||||||
|
avg_item_size | 16
|
||||||
|
page_size | 8192
|
||||||
|
free_size | 808
|
||||||
|
btpo_prev | 1
|
||||||
|
btpo_next | 4
|
||||||
|
btpo_level | 0
|
||||||
|
btpo_flags | 1
|
||||||
|
-[ RECORD 3 ]-+-----
|
||||||
|
blkno | 3
|
||||||
|
type | r
|
||||||
|
live_items | 3
|
||||||
|
dead_items | 0
|
||||||
|
avg_item_size | 13
|
||||||
|
page_size | 8192
|
||||||
|
free_size | 8096
|
||||||
|
btpo_prev | 0
|
||||||
|
btpo_next | 0
|
||||||
|
btpo_level | 1
|
||||||
|
btpo_flags | 2
|
||||||
|
-[ RECORD 4 ]-+-----
|
||||||
|
blkno | 4
|
||||||
|
type | l
|
||||||
|
live_items | 268
|
||||||
|
dead_items | 0
|
||||||
|
avg_item_size | 16
|
||||||
|
page_size | 8192
|
||||||
|
free_size | 2788
|
||||||
|
btpo_prev | 2
|
||||||
|
btpo_next | 0
|
||||||
|
btpo_level | 0
|
||||||
|
btpo_flags | 1
|
||||||
|
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 1, 0);
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 1, 2);
|
||||||
|
-[ RECORD 1 ]-+-----
|
||||||
|
blkno | 1
|
||||||
|
type | l
|
||||||
|
live_items | 367
|
||||||
|
dead_items | 0
|
||||||
|
avg_item_size | 16
|
||||||
|
page_size | 8192
|
||||||
|
free_size | 808
|
||||||
|
btpo_prev | 0
|
||||||
|
btpo_next | 2
|
||||||
|
btpo_level | 0
|
||||||
|
btpo_flags | 1
|
||||||
|
-[ RECORD 2 ]-+-----
|
||||||
|
blkno | 2
|
||||||
|
type | l
|
||||||
|
live_items | 367
|
||||||
|
dead_items | 0
|
||||||
|
avg_item_size | 16
|
||||||
|
page_size | 8192
|
||||||
|
free_size | 808
|
||||||
|
btpo_prev | 1
|
||||||
|
btpo_next | 4
|
||||||
|
btpo_level | 0
|
||||||
|
btpo_flags | 1
|
||||||
|
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 3, 2);
|
||||||
|
-[ RECORD 1 ]-+-----
|
||||||
|
blkno | 3
|
||||||
|
type | r
|
||||||
|
live_items | 3
|
||||||
|
dead_items | 0
|
||||||
|
avg_item_size | 13
|
||||||
|
page_size | 8192
|
||||||
|
free_size | 8096
|
||||||
|
btpo_prev | 0
|
||||||
|
btpo_next | 0
|
||||||
|
btpo_level | 1
|
||||||
|
btpo_flags | 2
|
||||||
|
-[ RECORD 2 ]-+-----
|
||||||
|
blkno | 4
|
||||||
|
type | l
|
||||||
|
live_items | 268
|
||||||
|
dead_items | 0
|
||||||
|
avg_item_size | 16
|
||||||
|
page_size | 8192
|
||||||
|
free_size | 2788
|
||||||
|
btpo_prev | 2
|
||||||
|
btpo_next | 0
|
||||||
|
btpo_level | 0
|
||||||
|
btpo_flags | 1
|
||||||
|
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 7, 2);
|
||||||
|
ERROR: block number 7 is out of range
|
||||||
|
DROP TABLE test2;
|
||||||
SELECT * FROM bt_page_items('test1_a_idx', -1);
|
SELECT * FROM bt_page_items('test1_a_idx', -1);
|
||||||
ERROR: invalid block number
|
ERROR: invalid block number -1
|
||||||
SELECT * FROM bt_page_items('test1_a_idx', 0);
|
SELECT * FROM bt_page_items('test1_a_idx', 0);
|
||||||
ERROR: block 0 is a meta page
|
ERROR: block 0 is a meta page
|
||||||
SELECT * FROM bt_page_items('test1_a_idx', 1);
|
SELECT * FROM bt_page_items('test1_a_idx', 1);
|
||||||
@ -51,7 +164,7 @@ htid | (0,1)
|
|||||||
tids |
|
tids |
|
||||||
|
|
||||||
SELECT * FROM bt_page_items('test1_a_idx', 2);
|
SELECT * FROM bt_page_items('test1_a_idx', 2);
|
||||||
ERROR: block number out of range
|
ERROR: block number 2 is out of range
|
||||||
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', -1));
|
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', -1));
|
||||||
ERROR: invalid block number
|
ERROR: invalid block number
|
||||||
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0));
|
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0));
|
||||||
|
@ -36,6 +36,7 @@ install_data(
|
|||||||
'pageinspect--1.8--1.9.sql',
|
'pageinspect--1.8--1.9.sql',
|
||||||
'pageinspect--1.9--1.10.sql',
|
'pageinspect--1.9--1.10.sql',
|
||||||
'pageinspect--1.10--1.11.sql',
|
'pageinspect--1.10--1.11.sql',
|
||||||
|
'pageinspect--1.11--1.12.sql',
|
||||||
'pageinspect.control',
|
'pageinspect.control',
|
||||||
kwargs: contrib_data_args,
|
kwargs: contrib_data_args,
|
||||||
)
|
)
|
||||||
|
23
contrib/pageinspect/pageinspect--1.11--1.12.sql
Normal file
23
contrib/pageinspect/pageinspect--1.11--1.12.sql
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/* contrib/pageinspect/pageinspect--1.11--1.12.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.12'" to load this file. \quit
|
||||||
|
|
||||||
|
--
|
||||||
|
-- bt_multi_page_stats()
|
||||||
|
--
|
||||||
|
CREATE FUNCTION bt_multi_page_stats(IN relname text, IN blkno int8, IN blk_count int8,
|
||||||
|
OUT blkno int8,
|
||||||
|
OUT type "char",
|
||||||
|
OUT live_items int4,
|
||||||
|
OUT dead_items int4,
|
||||||
|
OUT avg_item_size int4,
|
||||||
|
OUT page_size int4,
|
||||||
|
OUT free_size int4,
|
||||||
|
OUT btpo_prev int8,
|
||||||
|
OUT btpo_next int8,
|
||||||
|
OUT btpo_level int8,
|
||||||
|
OUT btpo_flags int4)
|
||||||
|
RETURNS SETOF record
|
||||||
|
AS 'MODULE_PATHNAME', 'bt_multi_page_stats'
|
||||||
|
LANGUAGE C STRICT PARALLEL RESTRICTED;
|
@ -1,5 +1,5 @@
|
|||||||
# pageinspect extension
|
# pageinspect extension
|
||||||
comment = 'inspect the contents of database pages at a low level'
|
comment = 'inspect the contents of database pages at a low level'
|
||||||
default_version = '1.11'
|
default_version = '1.12'
|
||||||
module_pathname = '$libdir/pageinspect'
|
module_pathname = '$libdir/pageinspect'
|
||||||
relocatable = true
|
relocatable = true
|
||||||
|
@ -11,6 +11,17 @@ SELECT * FROM bt_page_stats('test1_a_idx', 0);
|
|||||||
SELECT * FROM bt_page_stats('test1_a_idx', 1);
|
SELECT * FROM bt_page_stats('test1_a_idx', 1);
|
||||||
SELECT * FROM bt_page_stats('test1_a_idx', 2);
|
SELECT * FROM bt_page_stats('test1_a_idx', 2);
|
||||||
|
|
||||||
|
-- bt_multi_page_stats() function returns a set of records of page statistics.
|
||||||
|
CREATE TABLE test2 AS (SELECT generate_series(1, 1000)::int8 AS col1);
|
||||||
|
CREATE INDEX test2_col1_idx ON test2(col1);
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 0, 1);
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 1, -1);
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 1, 0);
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 1, 2);
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 3, 2);
|
||||||
|
SELECT * FROM bt_multi_page_stats('test2_col1_idx', 7, 2);
|
||||||
|
DROP TABLE test2;
|
||||||
|
|
||||||
SELECT * FROM bt_page_items('test1_a_idx', -1);
|
SELECT * FROM bt_page_items('test1_a_idx', -1);
|
||||||
SELECT * FROM bt_page_items('test1_a_idx', 0);
|
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', 1);
|
||||||
|
@ -326,7 +326,7 @@ allequalimage | f
|
|||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
<function>bt_page_stats</function> returns summary information about
|
<function>bt_page_stats</function> returns summary information about
|
||||||
single pages of B-tree indexes. For example:
|
a data page of a B-tree index. For example:
|
||||||
<screen>
|
<screen>
|
||||||
test=# SELECT * FROM bt_page_stats('pg_cast_oid_index', 1);
|
test=# SELECT * FROM bt_page_stats('pg_cast_oid_index', 1);
|
||||||
-[ RECORD 1 ]-+-----
|
-[ RECORD 1 ]-+-----
|
||||||
@ -346,6 +346,54 @@ btpo_flags | 3
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<function>bt_multi_page_stats(relname text, blkno bigint, blk_count bigint) returns setof record</function>
|
||||||
|
<indexterm>
|
||||||
|
<primary>bt_multi_page_stats</primary>
|
||||||
|
</indexterm>
|
||||||
|
</term>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<function>bt_multi_page_stats</function> returns the same information
|
||||||
|
as <function>bt_page_stats</function>, but does so for each page of the
|
||||||
|
range of pages beginning at <parameter>blkno</parameter> and extending
|
||||||
|
for <parameter>blk_count</parameter> pages.
|
||||||
|
If <parameter>blk_count</parameter> is negative, all pages
|
||||||
|
from <parameter>blkno</parameter> to the end of the index are reported
|
||||||
|
on. For example:
|
||||||
|
<screen>
|
||||||
|
test=# SELECT * FROM bt_multi_page_stats('pg_proc_oid_index', 5, 2);
|
||||||
|
-[ RECORD 1 ]-+-----
|
||||||
|
blkno | 5
|
||||||
|
type | l
|
||||||
|
live_items | 367
|
||||||
|
dead_items | 0
|
||||||
|
avg_item_size | 16
|
||||||
|
page_size | 8192
|
||||||
|
free_size | 808
|
||||||
|
btpo_prev | 4
|
||||||
|
btpo_next | 6
|
||||||
|
btpo_level | 0
|
||||||
|
btpo_flags | 1
|
||||||
|
-[ RECORD 2 ]-+-----
|
||||||
|
blkno | 6
|
||||||
|
type | l
|
||||||
|
live_items | 367
|
||||||
|
dead_items | 0
|
||||||
|
avg_item_size | 16
|
||||||
|
page_size | 8192
|
||||||
|
free_size | 808
|
||||||
|
btpo_prev | 5
|
||||||
|
btpo_next | 7
|
||||||
|
btpo_level | 0
|
||||||
|
btpo_flags | 1
|
||||||
|
</screen>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>
|
<term>
|
||||||
<function>bt_page_items(relname text, blkno bigint) returns setof record</function>
|
<function>bt_page_items(relname text, blkno bigint) returns setof record</function>
|
||||||
|
Reference in New Issue
Block a user