From dfd8a953be512658f0341d247d48679bde294362 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan Date: Mon, 10 Sep 2018 19:53:51 -0700 Subject: [PATCH v17 7/7] DEBUG: Add pageinspect instrumentation. Have pageinspect display user-visible attribute values. 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 | 68 +++++++++++++++---- contrib/pageinspect/expected/btree.out | 3 +- contrib/pageinspect/pageinspect--1.6--1.7.sql | 22 ++++++ 3 files changed, 79 insertions(+), 14 deletions(-) diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c index 8d27c9b0f6..95c81c0808 100644 --- a/contrib/pageinspect/btreefuncs.c +++ b/contrib/pageinspect/btreefuncs.c @@ -29,6 +29,7 @@ #include "pageinspect.h" +#include "access/genam.h" #include "access/nbtree.h" #include "access/relation.h" #include "catalog/namespace.h" @@ -243,6 +244,7 @@ bt_page_stats(PG_FUNCTION_ARGS) */ struct user_args { + Relation rel; Page page; OffsetNumber offset; }; @@ -254,9 +256,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[7]; HeapTuple tuple; ItemId id; IndexTuple itup; @@ -265,6 +267,8 @@ bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset) int dlen; char *dump; char *ptr; + ItemPointer htid; + BTPageOpaque opaque; id = PageGetItemId(page, offset); @@ -283,16 +287,53 @@ 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; + + natts = BTreeTupleGetNAtts(itup, rel); + + itupdesc->natts = natts; + memset(&isnull, 0xFF, sizeof(isnull)); + index_deform_tuple(itup, itupdesc, datvalues, isnull); + indnkeyatts = rel->rd_index->indnkeyatts; + 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; + } + } + + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + if (P_ISLEAF(opaque) && offset >= P_FIRSTDATAKEY(opaque)) + htid = &itup->t_tid; + else if (_bt_heapkeyspace(rel)) + htid = BTreeTupleGetHeapTID(itup); + else + htid = NULL; + + if (htid) + values[j] = psprintf("(%u,%u)", + ItemPointerGetBlockNumberNoCheck(htid), + ItemPointerGetOffsetNumberNoCheck(htid)); + else + values[j] = NULL; tuple = BuildTupleFromCStrings(fctx->attinmeta, values); @@ -366,11 +407,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; @@ -397,12 +438,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); @@ -482,7 +524,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..067e73f21a 100644 --- a/contrib/pageinspect/expected/btree.out +++ b/contrib/pageinspect/expected/btree.out @@ -40,7 +40,8 @@ ctid | (0,1) itemlen | 16 nulls | f vars | f -data | 01 00 00 00 00 00 00 01 +data | (a)=(72057594037927937) +htid | (0,1) 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..9acbad1589 100644 --- a/contrib/pageinspect/pageinspect--1.6--1.7.sql +++ b/contrib/pageinspect/pageinspect--1.6--1.7.sql @@ -24,3 +24,25 @@ 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) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'bt_page_items' +LANGUAGE C STRICT PARALLEL SAFE; -- 2.17.1