diff options
Diffstat (limited to 'contrib/pageinspect/btreefuncs.c')
-rw-r--r-- | contrib/pageinspect/btreefuncs.c | 402 |
1 files changed, 185 insertions, 217 deletions
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c index f02c3821834..e8063d42c62 100644 --- a/contrib/pageinspect/btreefuncs.c +++ b/contrib/pageinspect/btreefuncs.c @@ -24,36 +24,24 @@ #include "postgres.h" -#include "fmgr.h" -#include "funcapi.h" #include "access/heapam.h" -#include "access/itup.h" #include "access/nbtree.h" -#include "access/transam.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" +#include "funcapi.h" +#include "miscadmin.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_page_items(PG_FUNCTION_ARGS); extern Datum bt_page_stats(PG_FUNCTION_ARGS); -#define BTMETAP_TYPE "public.bt_metap_type" -#define BTMETAP_NCOLUMNS 6 - -#define BTPAGEITEMS_TYPE "public.bt_page_items_type" -#define BTPAGEITEMS_NCOLUMNS 6 - -#define BTPAGESTATS_TYPE "public.bt_page_stats_type" -#define BTPAGESTATS_NCOLUMNS 11 - +PG_FUNCTION_INFO_V1(bt_metap); +PG_FUNCTION_INFO_V1(bt_page_items); +PG_FUNCTION_INFO_V1(bt_page_stats); -#define IS_INDEX(r) ((r)->rd_rel->relkind == 'i') +#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) { \ @@ -93,42 +81,11 @@ typedef struct BTPageStat BTCycleId btpo_cycleid; } 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() * - * Collect statistics of single b-tree leaf page + * Collect statistics of single b-tree page * ------------------------------------------------- */ static void @@ -199,7 +156,7 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat * stat) /* ----------------------------------------------- * bt_page() * - * Usage: SELECT * FROM bt_page('t1_pkey', 0); + * Usage: SELECT * FROM bt_page('t1_pkey', 1); * ----------------------------------------------- */ Datum @@ -208,75 +165,75 @@ bt_page_stats(PG_FUNCTION_ARGS) 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); - CHECK_RELATION_BLOCK_RANGE(rel, blkno); - - buffer = ReadBuffer(rel, blkno); - 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) elog(ERROR, "block 0 is a meta page"); - { - HeapTuple tuple; - TupleDesc tupleDesc; - int j; - char *values[BTPAGESTATS_NCOLUMNS]; - - BTPageStat stat; - - /* 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); - - tupleDesc = RelationNameGetTupleDesc(BTPAGESTATS_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); + CHECK_RELATION_BLOCK_RANGE(rel, blkno); - 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); + buffer = ReadBuffer(rel, blkno); - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", stat.btpo_flags); + /* 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); + + /* 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); + tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc), + values); - result = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple); - } + result = HeapTupleGetDatum(tuple); ReleaseBuffer(buffer); @@ -288,22 +245,19 @@ bt_page_stats(PG_FUNCTION_ARGS) /*------------------------------------------------------- * 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 { - TupleDesc tupd; - Relation rel; - Buffer buffer; Page page; - uint16 offset; + OffsetNumber offset; }; Datum @@ -311,42 +265,58 @@ bt_page_items(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); uint32 blkno = PG_GETARG_UINT32(1); - - RangeVar *relrv; Datum result; - char *values[BTPAGEITEMS_NCOLUMNS]; - BTPageOpaque opaque; + char *values[6]; HeapTuple tuple; - ItemId id; - FuncCallContext *fctx; MemoryContext mctx; - struct user_args *uargs = NULL; + struct user_args *uargs; - if (blkno == 0) - elog(ERROR, "block 0 is a meta page"); + 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(); - mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); - uargs = palloc(sizeof(struct user_args)); + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); - uargs->tupd = RelationNameGetTupleDesc(BTPAGEITEMS_TYPE); - uargs->offset = FirstOffsetNumber; + if (!IS_INDEX(rel) || !IS_BTREE(rel)) + elog(ERROR, "relation \"%s\" is not a btree index", + RelationGetRelationName(rel)); - relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); - uargs->rel = relation_openrv(relrv, AccessShareLock); + if (blkno == 0) + elog(ERROR, "block 0 is a meta page"); + + CHECK_RELATION_BLOCK_RANGE(rel, blkno); - CHECK_RELATION_BLOCK_RANGE(uargs->rel, blkno); + buffer = ReadBuffer(rel, blkno); - uargs->buffer = ReadBuffer(uargs->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); + + uargs = palloc(sizeof(struct user_args)); + + uargs->page = palloc(BLCKSZ); + memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ); - if (!IS_INDEX(uargs->rel) || !IS_BTREE(uargs->rel)) - elog(ERROR, "bt_page_items() can only be used on b-tree index"); + ReleaseBuffer(buffer); + relation_close(rel, AccessShareLock); - uargs->page = BufferGetPage(uargs->buffer); + uargs->offset = FirstOffsetNumber; opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); @@ -354,6 +324,13 @@ bt_page_items(PG_FUNCTION_ARGS) 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); @@ -364,7 +341,13 @@ 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); @@ -373,60 +356,43 @@ bt_page_items(PG_FUNCTION_ARGS) 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++) { - int j = 0; - - BlockNumber blkno = BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)); - - values[j] = palloc(32); - snprintf(values[j++], 32, "%d", uargs->offset); - values[j] = palloc(32); - snprintf(values[j++], 32, "(%u,%u)", blkno, 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'); - - { - int off; - char *dump; - 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; - } - - tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(uargs->tupd), values); - result = TupleGetDatum(TupleDescGetSlot(uargs->tupd), tuple); + 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 { - ReleaseBuffer(uargs->buffer); - relation_close(uargs->rel, AccessShareLock); - + pfree(uargs->page); + pfree(uargs); SRF_RETURN_DONE(fctx); } } @@ -435,7 +401,7 @@ bt_page_items(PG_FUNCTION_ARGS) /* ------------------------------------------------ * bt_metap() * - * Get a btree meta-page information + * Get a btree's meta-page information * * Usage: SELECT * FROM bt_metap('t1_pkey') * ------------------------------------------------ @@ -444,53 +410,55 @@ Datum bt_metap(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_P(0); - Buffer buffer; - + Datum result; Relation rel; 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)); rel = relation_openrv(relrv, AccessShareLock); 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); - - { - BTMetaPageData *metad; - - TupleDesc tupleDesc; - int j; - char *values[BTMETAP_NCOLUMNS]; - HeapTuple tuple; - - Page page = BufferGetPage(buffer); - - metad = BTPageGetMeta(page); - - tupleDesc = RelationNameGetTupleDesc(BTMETAP_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 = TupleGetDatum(TupleDescGetSlot(tupleDesc), tuple); - } + 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); ReleaseBuffer(buffer); |