From d72829b729b2e028048ebc9fcbdb9a7f47c724b8 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan Date: Mon, 10 Sep 2018 19:53:51 -0700 Subject: [PATCH v21 3/3] DEBUG: Add pageinspect instrumentation. Have pageinspect display user-visible attribute values, heap TID, max heap TID, and the number of TIDs in a tuple (can be > 1 in the case of posting list tuples). Also adds a column that shows whether or not the LP_DEAD bit has been set. This patch is not proposed for inclusion in PostgreSQL; it's included for the convenience of reviewers. The following query can be used with this hacked pageinspect, which visualizes the internal pages: """ with recursive index_details as ( select 'my_test_index'::text idx ), size_in_pages_index as ( select (pg_relation_size(idx::regclass) / (2^13))::int4 size_pages from index_details ), page_stats as ( select index_details.*, stats.* from index_details, size_in_pages_index, lateral (select i from generate_series(1, size_pages - 1) i) series, lateral (select * from bt_page_stats(idx, i)) stats), internal_page_stats as ( select * from page_stats where type != 'l'), meta_stats as ( select * from index_details s, lateral (select * from bt_metap(s.idx)) meta), internal_items as ( select * from internal_page_stats order by btpo desc), -- XXX: Note ordering dependency within this CTE, on internal_items ordered_internal_items(item, blk, level) as ( select 1, blkno, btpo from internal_items where btpo_prev = 0 and btpo = (select level from meta_stats) union select case when level = btpo then o.item + 1 else 1 end, blkno, btpo from internal_items i, ordered_internal_items o where i.btpo_prev = o.blk or (btpo_prev = 0 and btpo = o.level - 1) ) select --idx, btpo as level, item as l_item, blkno, --btpo_prev, --btpo_next, btpo_flags, type, live_items, dead_items, avg_item_size, page_size, free_size, -- Only non-rightmost pages have high key. Show heap TID for both pivot and non-pivot tuples here. case when btpo_next != 0 then (select data || coalesce(', (htid)=(''' || htid || ''')', '') from bt_page_items(idx, blkno) where itemoffset = 1) end as highkey from ordered_internal_items o join internal_items i on o.blk = i.blkno order by btpo desc, item; """ --- contrib/pageinspect/btreefuncs.c | 92 ++++++++++++++++--- contrib/pageinspect/expected/btree.out | 6 +- contrib/pageinspect/pageinspect--1.6--1.7.sql | 25 +++++ 3 files changed, 109 insertions(+), 14 deletions(-) diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c index 78cdc69ec7..435e71ae22 100644 --- a/contrib/pageinspect/btreefuncs.c +++ b/contrib/pageinspect/btreefuncs.c @@ -27,6 +27,7 @@ #include "postgres.h" +#include "access/genam.h" #include "access/nbtree.h" #include "access/relation.h" #include "catalog/namespace.h" @@ -241,6 +242,7 @@ bt_page_stats(PG_FUNCTION_ARGS) */ struct user_args { + Relation rel; Page page; OffsetNumber offset; }; @@ -252,9 +254,9 @@ struct user_args * ------------------------------------------------------ */ static Datum -bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset) +bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset, Relation rel) { - char *values[6]; + char *values[10]; HeapTuple tuple; ItemId id; IndexTuple itup; @@ -263,6 +265,8 @@ bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset) int dlen; char *dump; char *ptr; + ItemPointer min_htid, + max_htid; id = PageGetItemId(page, offset); @@ -281,16 +285,77 @@ bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset) 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 (rel) { - if (off > 0) - *dump++ = ' '; - sprintf(dump, "%02x", *(ptr + off) & 0xff); - dump += 2; + TupleDesc itupdesc = RelationGetDescr(rel); + Datum datvalues[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + int natts; + int indnkeyatts = rel->rd_index->indnkeyatts; + + natts = BTreeTupleGetNAtts(itup, rel); + + itupdesc->natts = Min(indnkeyatts, natts); + memset(&isnull, 0xFF, sizeof(isnull)); + index_deform_tuple(itup, itupdesc, datvalues, isnull); + rel->rd_index->indnkeyatts = natts; + values[j++] = BuildIndexValueDescription(rel, datvalues, isnull); + itupdesc->natts = IndexRelationGetNumberOfAttributes(rel); + rel->rd_index->indnkeyatts = indnkeyatts; } + else + { + 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; + } + } + + if (rel && !_bt_heapkeyspace(rel)) + { + min_htid = NULL; + max_htid = NULL; + } + else + { + min_htid = BTreeTupleGetHeapTID(itup); + if (BTreeTupleIsPosting(itup)) + max_htid = BTreeTupleGetMaxHeapTID(itup); + else + max_htid = NULL; + } + + if (min_htid) + values[j++] = psprintf("(%u,%u)", + ItemPointerGetBlockNumberNoCheck(min_htid), + ItemPointerGetOffsetNumberNoCheck(min_htid)); + else + values[j++] = NULL; + + if (max_htid) + values[j++] = psprintf("(%u,%u)", + ItemPointerGetBlockNumberNoCheck(max_htid), + ItemPointerGetOffsetNumberNoCheck(max_htid)); + else + values[j++] = NULL; + + if (min_htid == NULL) + values[j++] = psprintf("0"); + else if (!BTreeTupleIsPosting(itup)) + values[j++] = psprintf("1"); + else + values[j++] = psprintf("%d", (int) BTreeTupleGetNPosting(itup)); + + if (!ItemIdIsDead(id)) + values[j++] = psprintf("f"); + else + values[j++] = psprintf("t"); tuple = BuildTupleFromCStrings(fctx->attinmeta, values); @@ -364,11 +429,11 @@ bt_page_items(PG_FUNCTION_ARGS) uargs = palloc(sizeof(struct user_args)); + uargs->rel = rel; uargs->page = palloc(BLCKSZ); memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ); UnlockReleaseBuffer(buffer); - relation_close(rel, AccessShareLock); uargs->offset = FirstOffsetNumber; @@ -395,12 +460,13 @@ bt_page_items(PG_FUNCTION_ARGS) if (fctx->call_cntr < fctx->max_calls) { - result = bt_page_print_tuples(fctx, uargs->page, uargs->offset); + result = bt_page_print_tuples(fctx, uargs->page, uargs->offset, uargs->rel); uargs->offset++; SRF_RETURN_NEXT(fctx, result); } else { + relation_close(uargs->rel, AccessShareLock); pfree(uargs->page); pfree(uargs); SRF_RETURN_DONE(fctx); @@ -480,7 +546,7 @@ bt_page_items_bytea(PG_FUNCTION_ARGS) if (fctx->call_cntr < fctx->max_calls) { - result = bt_page_print_tuples(fctx, uargs->page, uargs->offset); + result = bt_page_print_tuples(fctx, uargs->page, uargs->offset, NULL); uargs->offset++; SRF_RETURN_NEXT(fctx, result); } diff --git a/contrib/pageinspect/expected/btree.out b/contrib/pageinspect/expected/btree.out index 07c2dcd771..0f6dccaadc 100644 --- a/contrib/pageinspect/expected/btree.out +++ b/contrib/pageinspect/expected/btree.out @@ -40,7 +40,11 @@ ctid | (0,1) itemlen | 16 nulls | f vars | f -data | 01 00 00 00 00 00 00 01 +data | (a)=(72057594037927937) +htid | (0,1) +max_htid | +nheap_tids | 1 +isdead | f SELECT * FROM bt_page_items('test1_a_idx', 2); ERROR: block number out of range diff --git a/contrib/pageinspect/pageinspect--1.6--1.7.sql b/contrib/pageinspect/pageinspect--1.6--1.7.sql index 2433a21af2..00473da938 100644 --- a/contrib/pageinspect/pageinspect--1.6--1.7.sql +++ b/contrib/pageinspect/pageinspect--1.6--1.7.sql @@ -24,3 +24,28 @@ CREATE FUNCTION bt_metap(IN relname text, OUT last_cleanup_num_tuples real) AS 'MODULE_PATHNAME', 'bt_metap' LANGUAGE C STRICT PARALLEL SAFE; + +-- +-- bt_page_items() +-- +DROP FUNCTION bt_page_items(IN relname text, IN blkno int4, + OUT itemoffset smallint, + OUT ctid tid, + OUT itemlen smallint, + OUT nulls bool, + OUT vars bool, + OUT data text); +CREATE FUNCTION bt_page_items(IN relname text, IN blkno int4, + OUT itemoffset smallint, + OUT ctid tid, + OUT itemlen smallint, + OUT nulls bool, + OUT vars bool, + OUT data text, + OUT htid tid, + OUT max_htid tid, + OUT nheap_tids int4, + OUT isdead boolean) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'bt_page_items' +LANGUAGE C STRICT PARALLEL SAFE; -- 2.17.1