On Tue, Nov 4, 2014 at 7:26 AM, Amit Kapila <amit.kapil...@gmail.com> wrote: > I think these functions will be quite useful for debugging purpose > and we already have similar function's for other index (btree).
This patch has bitrotted. I attach rebased revision, for the convenience of others - V1.3 of pageinspect will now incorporate both GIN stuff, and BRIN stuff. Seems like this patch was affected by the recent problems with header includes - that's fixed. Do you intend to fix this up? + /* TODO: array of decoded item pointers */ + nulls[2] = true; -- Peter Geoghegan
From 5d10bfe4f6db5e37dcf087d62f42cbc6c9423c26 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan <p...@heroku.com> Date: Tue, 18 Nov 2014 13:38:12 -0800 Subject: [PATCH] Add pageinspect functions for inspecting GIN indexes. --- contrib/pageinspect/Makefile | 2 +- contrib/pageinspect/ginfuncs.c | 265 ++++++++++++++++++++++++++ contrib/pageinspect/pageinspect--1.2--1.3.sql | 37 ++++ contrib/pageinspect/pageinspect--1.3.sql | 42 ++++ 4 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 contrib/pageinspect/ginfuncs.c diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index a59de8a..e651543 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -1,7 +1,7 @@ # contrib/pageinspect/Makefile MODULE_big = pageinspect -OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o brinfuncs.o $(WIN32RES) +OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o brinfuncs.o ginfuncs.o $(WIN32RES) EXTENSION = pageinspect DATA = pageinspect--1.3.sql pageinspect--1.0--1.1.sql \ diff --git a/contrib/pageinspect/ginfuncs.c b/contrib/pageinspect/ginfuncs.c new file mode 100644 index 0000000..0602873 --- /dev/null +++ b/contrib/pageinspect/ginfuncs.c @@ -0,0 +1,265 @@ +/* + * contrib/pageinspect/ginfuncs.c + */ + +#include "postgres.h" + +#include "access/gin.h" +#include "access/gin_private.h" +#include "access/htup_details.h" +#include "catalog/namespace.h" +#include "catalog/pg_type.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/rel.h" + +#define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X)) +#define ItemPointerGetDatum(X) PointerGetDatum(X) + +PG_FUNCTION_INFO_V1(gin_metapage); +PG_FUNCTION_INFO_V1(gin_pageopaq); +PG_FUNCTION_INFO_V1(gin_dataleafpage); + +Datum +gin_metapage(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + int raw_page_size; + TupleDesc tupdesc; + Page page; + GinPageOpaque opaq; + GinMetaPageData *metadata; + HeapTuple resultTuple; + Datum values[10]; + bool nulls[10]; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + raw_page_size = VARSIZE(raw_page) - VARHDRSZ; + if (raw_page_size < BLCKSZ) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page too small (%d bytes)", raw_page_size))); + page = VARDATA(raw_page); + + opaq = (GinPageOpaque) PageGetSpecialPointer(page); + if (opaq->flags != GIN_META) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a GIN metapage"), + errdetail("Flags %04X, expected %04X", + opaq->flags, GIN_META))); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + metadata = GinPageGetMeta(page); + + memset(nulls, 0, sizeof(nulls)); + + values[0] = Int64GetDatum(metadata->head); + values[1] = Int64GetDatum(metadata->tail); + values[2] = Int32GetDatum(metadata->tailFreeSize); + values[3] = Int64GetDatum(metadata->nPendingPages); + values[4] = Int64GetDatum(metadata->nPendingHeapTuples); + + /* statistics, updated by VACUUM */ + values[5] = Int64GetDatum(metadata->nTotalPages); + values[6] = Int64GetDatum(metadata->nEntryPages); + values[7] = Int64GetDatum(metadata->nDataPages); + values[8] = Int64GetDatum(metadata->nEntries); + + values[9] = Int32GetDatum(metadata->ginVersion); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(tupdesc, values, nulls); + + return HeapTupleGetDatum(resultTuple); +} + + +Datum +gin_pageopaq(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + int raw_page_size; + TupleDesc tupdesc; + Page page; + GinPageOpaque opaq; + HeapTuple resultTuple; + Datum values[3]; + bool nulls[10]; + Datum flags[16]; + int nflags = 0; + uint16 flagbits; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + raw_page_size = VARSIZE(raw_page) - VARHDRSZ; + if (raw_page_size < BLCKSZ) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page too small (%d bytes)", raw_page_size))); + page = VARDATA(raw_page); + + opaq = (GinPageOpaque) PageGetSpecialPointer(page); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Convert the flags bitmask to an array of human-readable names */ + flagbits = opaq->flags; + if (flagbits & GIN_DATA) + flags[nflags++] = CStringGetTextDatum("data"); + if (flagbits & GIN_LEAF) + flags[nflags++] = CStringGetTextDatum("leaf"); + if (flagbits & GIN_DELETED) + flags[nflags++] = CStringGetTextDatum("deleted"); + if (flagbits & GIN_META) + flags[nflags++] = CStringGetTextDatum("meta"); + if (flagbits & GIN_LIST) + flags[nflags++] = CStringGetTextDatum("list"); + if (flagbits & GIN_LIST_FULLROW) + flags[nflags++] = CStringGetTextDatum("list_fullrow"); + if (flagbits & GIN_INCOMPLETE_SPLIT) + flags[nflags++] = CStringGetTextDatum("incomplete_split"); + if (flagbits & GIN_COMPRESSED) + flags[nflags++] = CStringGetTextDatum("compressed"); + flagbits &= ~(GIN_DATA | GIN_LEAF | GIN_DELETED | GIN_META | GIN_LIST | + GIN_LIST_FULLROW | GIN_INCOMPLETE_SPLIT | GIN_COMPRESSED); + if (flagbits) + { + /* any flags we don't recognize are printed in hex */ + flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits)); } + + memset(nulls, 0, sizeof(nulls)); + + values[0] = Int64GetDatum(opaq->rightlink); + values[1] = Int64GetDatum(opaq->maxoff); + values[2] = PointerGetDatum( + construct_array(flags, nflags, TEXTOID, -1, false, 'i')); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(tupdesc, values, nulls); + + return HeapTupleGetDatum(resultTuple); +} + +/* ----------------------------------------------- + * gin_dataleafpage() + * + * Usage: SELECT * FROM gin_dataleafpage(get_raw_page('indexname', 1)); + * ----------------------------------------------- + */ + +typedef struct gin_dataleafpage_state +{ + TupleDesc tupd; + GinPostingList *seg; + GinPostingList *lastseg; +} gin_dataleafpage_state; + +Datum +gin_dataleafpage(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + int raw_page_size; + FuncCallContext *fctx; + gin_dataleafpage_state *inter_call_data; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + raw_page_size = VARSIZE(raw_page) - VARHDRSZ; + + if (SRF_IS_FIRSTCALL()) + { + TupleDesc tupdesc; + MemoryContext mctx; + Page page; + GinPageOpaque opaq; + + if (raw_page_size < BLCKSZ) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page too small (%d bytes)", raw_page_size))); + page = VARDATA(raw_page); + + if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a valid GIN data leaf page"), + errdetail("Special size %d, expected %d", + (int) PageGetSpecialSize(page), + (int) MAXALIGN(sizeof(GinPageOpaqueData))))); + + opaq = (GinPageOpaque) PageGetSpecialPointer(page); + if (opaq->flags != (GIN_DATA | GIN_LEAF | GIN_COMPRESSED)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a compressed GIN data leaf page"), + errdetail("Flags %04X, expected %04X", + opaq->flags, + (GIN_DATA | GIN_LEAF | GIN_COMPRESSED)))); + + fctx = SRF_FIRSTCALL_INIT(); + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + inter_call_data = palloc(sizeof(gin_dataleafpage_state)); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + inter_call_data->tupd = tupdesc; + + inter_call_data->seg = GinDataLeafPageGetPostingList(page); + inter_call_data->lastseg = (GinPostingList *) + (((char *) inter_call_data->seg) + + GinDataLeafPageGetPostingListSize(page)); + + fctx->user_fctx = inter_call_data; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + inter_call_data = fctx->user_fctx; + + if (inter_call_data->seg != inter_call_data->lastseg) + { + HeapTuple resultTuple; + Datum result; + Datum values[3]; + bool nulls[3]; + + memset(nulls, 0, sizeof(nulls)); + + values[0] = ItemPointerGetDatum(&inter_call_data->seg->first); + values[1] = UInt16GetDatum(inter_call_data->seg->nbytes); + /* TODO: array of decoded item pointers */ + nulls[2] = true; + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + inter_call_data->seg = GinNextPostingListSegment(inter_call_data->seg); + + SRF_RETURN_NEXT(fctx, result); + } + else + SRF_RETURN_DONE(fctx); +} diff --git a/contrib/pageinspect/pageinspect--1.2--1.3.sql b/contrib/pageinspect/pageinspect--1.2--1.3.sql index 9bc4dde..15eec67 100644 --- a/contrib/pageinspect/pageinspect--1.2--1.3.sql +++ b/contrib/pageinspect/pageinspect--1.2--1.3.sql @@ -40,4 +40,41 @@ CREATE FUNCTION brin_page_items(IN page bytea, IN index_oid regclass, OUT value text) RETURNS SETOF record AS 'MODULE_PATHNAME', 'brin_page_items' + +-- +-- gin_metapage() +-- +CREATE FUNCTION gin_metapage(IN page bytea, + OUT pending_head bigint, + OUT pending_tail bigint, + OUT tail_free_size int4, + OUT n_pending_pages bigint, + OUT n_pending_tuples bigint, + OUT n_total_pages bigint, + OUT n_entry_pages bigint, + OUT n_data_pages bigint, + OUT n_entries bigint, + OUT version int4) +AS 'MODULE_PATHNAME', 'gin_metapage' +LANGUAGE C STRICT; + +-- +-- gin_pageopaq() +-- +CREATE FUNCTION gin_pageopaq(IN page bytea, + OUT rightlink bigint, + OUT maxoff int4, + OUT flags text[]) +AS 'MODULE_PATHNAME', 'gin_pageopaq' +LANGUAGE C STRICT; + +-- +-- gin_dataleafpage() +-- +CREATE FUNCTION gin_dataleafpage(IN page bytea, + OUT first_tid tid, + OUT nbytes int2, + OUT tids tid[]) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'gin_dataleafpage' LANGUAGE C STRICT; diff --git a/contrib/pageinspect/pageinspect--1.3.sql b/contrib/pageinspect/pageinspect--1.3.sql index 856dcdf..8b7b096 100644 --- a/contrib/pageinspect/pageinspect--1.3.sql +++ b/contrib/pageinspect/pageinspect--1.3.sql @@ -144,3 +144,45 @@ CREATE FUNCTION fsm_page_contents(IN page bytea) RETURNS text AS 'MODULE_PATHNAME', 'fsm_page_contents' LANGUAGE C STRICT; + +-- +-- GIN functions +-- + +-- +-- gin_metapage() +-- +CREATE FUNCTION gin_metapage(IN page bytea, + OUT pending_head bigint, + OUT pending_tail bigint, + OUT tail_free_size int4, + OUT n_pending_pages bigint, + OUT n_pending_tuples bigint, + OUT n_total_pages bigint, + OUT n_entry_pages bigint, + OUT n_data_pages bigint, + OUT n_entries bigint, + OUT version int4) +AS 'MODULE_PATHNAME', 'gin_metapage' +LANGUAGE C STRICT; + +-- +-- gin_pageopaq() +-- +CREATE FUNCTION gin_pageopaq(IN page bytea, + OUT rightlink bigint, + OUT maxoff int4, + OUT flags text[]) +AS 'MODULE_PATHNAME', 'gin_pageopaq' +LANGUAGE C STRICT; + +-- +-- gin_dataleafpage() +-- +CREATE FUNCTION gin_dataleafpage(IN page bytea, + OUT first_tid tid, + OUT nbytes int2, + OUT tids tid[]) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'gin_dataleafpage' +LANGUAGE C STRICT; -- 1.9.1
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers