#include "postgres.h"
#include "access/nbtree.h"
#include "catalog/namespace.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/rel.h"
Go to the source code of this file.
Data Structures | |
struct | BTPageStat |
struct | user_args |
Defines | |
#define | IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX) |
#define | IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID) |
#define | CHECK_PAGE_OFFSET_RANGE(pg, offnum) |
#define | CHECK_RELATION_BLOCK_RANGE(rel, blkno) |
Typedefs | |
typedef struct BTPageStat | BTPageStat |
Functions | |
Datum | bt_metap (PG_FUNCTION_ARGS) |
Datum | bt_page_items (PG_FUNCTION_ARGS) |
Datum | bt_page_stats (PG_FUNCTION_ARGS) |
PG_FUNCTION_INFO_V1 (bt_metap) | |
PG_FUNCTION_INFO_V1 (bt_page_items) | |
PG_FUNCTION_INFO_V1 (bt_page_stats) | |
static void | GetBTPageStatistics (BlockNumber blkno, Buffer buffer, BTPageStat *stat) |
#define CHECK_PAGE_OFFSET_RANGE | ( | pg, | ||
offnum | ||||
) |
{ \ if ( !(FirstOffsetNumber <= (offnum) && \ (offnum) <= PageGetMaxOffsetNumber(pg)) ) \ elog(ERROR, "page offset number out of range"); }
Definition at line 49 of file btreefuncs.c.
#define CHECK_RELATION_BLOCK_RANGE | ( | rel, | ||
blkno | ||||
) |
{ \ if ( RelationGetNumberOfBlocks(rel) <= (BlockNumber) (blkno) ) \ elog(ERROR, "block number out of range"); }
Definition at line 55 of file btreefuncs.c.
Referenced by bt_page_items(), and bt_page_stats().
#define IS_BTREE | ( | r | ) | ((r)->rd_rel->relam == BTREE_AM_OID) |
Definition at line 47 of file btreefuncs.c.
Referenced by bt_metap(), bt_page_items(), and bt_page_stats().
#define IS_INDEX | ( | r | ) | ((r)->rd_rel->relkind == RELKIND_INDEX) |
Definition at line 46 of file btreefuncs.c.
Referenced by bt_metap(), bt_page_items(), and bt_page_stats().
typedef struct BTPageStat BTPageStat |
Datum bt_metap | ( | PG_FUNCTION_ARGS | ) |
Definition at line 433 of file btreefuncs.c.
References AccessShareLock, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_level, BTMetaPageData::btm_magic, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTPageGetMeta, BUFFER_LOCK_SHARE, BufferGetPage, BuildTupleFromCStrings(), elog, ereport, errcode(), errmsg(), ERROR, get_call_result_type(), HeapTupleGetDatum, IS_BTREE, IS_INDEX, LockBuffer(), makeRangeVarFromNameList(), NULL, user_args::page, palloc(), PG_GETARG_TEXT_P, PG_RETURN_DATUM, ReadBuffer(), relation_close(), RELATION_IS_OTHER_TEMP, relation_openrv(), RelationGetRelationName, snprintf(), superuser(), textToQualifiedNameList(), TupleDescGetAttInMetadata(), TYPEFUNC_COMPOSITE, UnlockReleaseBuffer(), and values.
{ text *relname = PG_GETARG_TEXT_P(0); Datum result; Relation rel; RangeVar *relrv; 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)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", RelationGetRelationName(rel)); /* * 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"))); buffer = ReadBuffer(rel, 0); LockBuffer(buffer, BUFFER_LOCK_SHARE); page = BufferGetPage(buffer); metad = BTPageGetMeta(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"); j = 0; values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_magic); values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_version); values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_root); values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_level); values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_fastroot); values[j] = palloc(32); snprintf(values[j++], 32, "%d", metad->btm_fastlevel); tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = HeapTupleGetDatum(tuple); UnlockReleaseBuffer(buffer); relation_close(rel, AccessShareLock); PG_RETURN_DATUM(result); }
Datum bt_page_items | ( | PG_FUNCTION_ARGS | ) |
Definition at line 276 of file btreefuncs.c.
References AccessShareLock, FuncCallContext::attinmeta, BlockIdGetBlockNumber, BUFFER_LOCK_SHARE, BufferGetPage, BuildTupleFromCStrings(), FuncCallContext::call_cntr, CHECK_RELATION_BLOCK_RANGE, elog, ereport, errcode(), errmsg(), ERROR, get_call_result_type(), HeapTupleGetDatum, IndexInfoFindDataOffset, IndexTupleHasNulls, IndexTupleHasVarwidths, IndexTupleSize, ItemPointerData::ip_blkid, ItemPointerData::ip_posid, IS_BTREE, IS_INDEX, ItemIdIsValid, LockBuffer(), makeRangeVarFromNameList(), FuncCallContext::max_calls, MemoryContextSwitchTo(), FuncCallContext::multi_call_memory_ctx, NOTICE, NULL, user_args::offset, P_ISDELETED, user_args::page, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageGetSpecialPointer, palloc(), palloc0(), pfree(), PG_GETARG_TEXT_P, PG_GETARG_UINT32, ReadBuffer(), relation_close(), RELATION_IS_OTHER_TEMP, relation_openrv(), RelationGetRelationName, snprintf(), SRF_FIRSTCALL_INIT, SRF_IS_FIRSTCALL, SRF_PERCALL_SETUP, SRF_RETURN_DONE, SRF_RETURN_NEXT, superuser(), IndexTupleData::t_info, IndexTupleData::t_tid, textToQualifiedNameList(), TupleDescGetAttInMetadata(), TYPEFUNC_COMPOSITE, UnlockReleaseBuffer(), FuncCallContext::user_fctx, and values.
{ text *relname = PG_GETARG_TEXT_P(0); uint32 blkno = PG_GETARG_UINT32(1); Datum result; char *values[6]; HeapTuple tuple; FuncCallContext *fctx; MemoryContext mctx; 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)); /* * 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) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = ReadBuffer(rel, blkno); LockBuffer(buffer, BUFFER_LOCK_SHARE); /* * 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); uargs = palloc(sizeof(struct user_args)); uargs->page = palloc(BLCKSZ); memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ); UnlockReleaseBuffer(buffer); relation_close(rel, AccessShareLock); uargs->offset = FirstOffsetNumber; opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->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) { 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] = palloc(32); snprintf(values[j++], 32, "%d", uargs->offset); values[j] = palloc(32); snprintf(values[j++], 32, "(%u,%u)", BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)), itup->t_tid.ip_posid); values[j] = palloc(32); snprintf(values[j++], 32, "%d", (int) IndexTupleSize(itup)); values[j] = palloc(32); snprintf(values[j++], 32, "%c", IndexTupleHasNulls(itup) ? 't' : 'f'); values[j] = palloc(32); snprintf(values[j++], 32, "%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; SRF_RETURN_NEXT(fctx, result); } else { pfree(uargs->page); pfree(uargs); SRF_RETURN_DONE(fctx); } }
Datum bt_page_stats | ( | PG_FUNCTION_ARGS | ) |
Definition at line 165 of file btreefuncs.c.
References AccessShareLock, BTPageStat::avg_item_size, BTPageStat::blkno, BTPageStat::btpo, BTPageStat::btpo_flags, BTPageStat::btpo_next, BTPageStat::btpo_prev, BUFFER_LOCK_SHARE, BuildTupleFromCStrings(), CHECK_RELATION_BLOCK_RANGE, BTPageStat::dead_items, elog, ereport, errcode(), errmsg(), ERROR, BTPageStat::free_size, get_call_result_type(), GetBTPageStatistics(), HeapTupleGetDatum, IS_BTREE, IS_INDEX, BTPageStat::level, BTPageStat::live_items, LockBuffer(), makeRangeVarFromNameList(), NULL, BTPageStat::page_size, palloc(), PG_GETARG_TEXT_P, PG_GETARG_UINT32, PG_RETURN_DATUM, ReadBuffer(), relation_close(), RELATION_IS_OTHER_TEMP, relation_openrv(), RelationGetRelationName, snprintf(), superuser(), textToQualifiedNameList(), TupleDescGetAttInMetadata(), BTPageStat::type, TYPEFUNC_COMPOSITE, UnlockReleaseBuffer(), values, and BTPageStat::xact.
{ text *relname = PG_GETARG_TEXT_P(0); uint32 blkno = PG_GETARG_UINT32(1); Buffer buffer; Relation rel; RangeVar *relrv; 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)); rel = relation_openrv(relrv, AccessShareLock); if (!IS_INDEX(rel) || !IS_BTREE(rel)) elog(ERROR, "relation \"%s\" is not a btree index", RelationGetRelationName(rel)); /* * 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) elog(ERROR, "block 0 is a meta page"); CHECK_RELATION_BLOCK_RANGE(rel, blkno); buffer = ReadBuffer(rel, 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(blkno, buffer, &stat); UnlockReleaseBuffer(buffer); relation_close(rel, AccessShareLock); /* 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] = palloc(32); snprintf(values[j++], 32, "%d", stat.blkno); values[j] = palloc(32); snprintf(values[j++], 32, "%c", stat.type); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.live_items); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.dead_items); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.avg_item_size); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.page_size); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.free_size); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.btpo_prev); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.btpo_next); values[j] = palloc(32); if (stat.type == 'd') snprintf(values[j++], 32, "%d", stat.btpo.xact); else snprintf(values[j++], 32, "%d", stat.btpo.level); values[j] = palloc(32); snprintf(values[j++], 32, "%d", stat.btpo_flags); tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), values); result = HeapTupleGetDatum(tuple); PG_RETURN_DATUM(result); }
static void GetBTPageStatistics | ( | BlockNumber | blkno, | |
Buffer | buffer, | |||
BTPageStat * | stat | |||
) | [static] |
Definition at line 94 of file btreefuncs.c.
References BTPageStat::avg_item_size, BTPageStat::blkno, BTPageOpaqueData::btpo, BTPageStat::btpo, BTPageOpaqueData::btpo_cycleid, BTPageStat::btpo_cycleid, BTPageOpaqueData::btpo_flags, BTPageStat::btpo_flags, BTPageOpaqueData::btpo_next, BTPageStat::btpo_next, BTPageOpaqueData::btpo_prev, BTPageStat::btpo_prev, BufferGetPage, BTPageStat::dead_items, FirstOffsetNumber, BTPageStat::free_size, IndexTupleSize, ItemIdIsDead, BTPageOpaqueData::level, BTPageStat::level, BTPageStat::live_items, BTPageStat::max_avail, P_IGNORE, P_ISDELETED, P_ISLEAF, P_ISROOT, BTPageStat::page_size, PageGetFreeSpace(), PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageGetPageSize, PageGetSpecialPointer, PageHeaderData::pd_special, BTPageStat::type, BTPageOpaqueData::xact, and BTPageStat::xact.
Referenced by bt_page_stats().
{ Page page = BufferGetPage(buffer); PageHeader phdr = (PageHeader) page; OffsetNumber maxoff = PageGetMaxOffsetNumber(page); BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page); int item_size = 0; int off; stat->blkno = blkno; stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData); stat->dead_items = stat->live_items = 0; stat->page_size = PageGetPageSize(page); /* page type (flags) */ if (P_ISDELETED(opaque)) { stat->type = 'd'; stat->btpo.xact = opaque->btpo.xact; return; } else if (P_IGNORE(opaque)) stat->type = 'e'; else if (P_ISLEAF(opaque)) stat->type = 'l'; else if (P_ISROOT(opaque)) stat->type = 'r'; else stat->type = 'i'; /* btpage opaque data */ stat->btpo_prev = opaque->btpo_prev; stat->btpo_next = opaque->btpo_next; stat->btpo.level = opaque->btpo.level; stat->btpo_flags = opaque->btpo_flags; stat->btpo_cycleid = opaque->btpo_cycleid; /* count live and dead tuples, and free space */ for (off = FirstOffsetNumber; off <= maxoff; off++) { IndexTuple itup; ItemId id = PageGetItemId(page, off); itup = (IndexTuple) PageGetItem(page, id); item_size += IndexTupleSize(itup); if (!ItemIdIsDead(id)) stat->live_items++; else stat->dead_items++; } stat->free_size = PageGetFreeSpace(page); if ((stat->live_items + stat->dead_items) > 0) stat->avg_item_size = item_size / (stat->live_items + stat->dead_items); else stat->avg_item_size = 0; }
PG_FUNCTION_INFO_V1 | ( | bt_metap | ) |
PG_FUNCTION_INFO_V1 | ( | bt_page_stats | ) |
PG_FUNCTION_INFO_V1 | ( | bt_page_items | ) |