diff options
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/pageinspect/Makefile | 5 | ||||
-rw-r--r-- | contrib/pageinspect/brinfuncs.c | 414 | ||||
-rw-r--r-- | contrib/pageinspect/pageinspect--1.2--1.3.sql | 43 | ||||
-rw-r--r-- | contrib/pageinspect/pageinspect--1.3.sql (renamed from contrib/pageinspect/pageinspect--1.2.sql) | 41 | ||||
-rw-r--r-- | contrib/pageinspect/pageinspect.control | 2 | ||||
-rw-r--r-- | contrib/pg_xlogdump/rmgrdesc.c | 1 |
6 files changed, 502 insertions, 4 deletions
diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index f10229db482..a59de8aba9e 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -1,10 +1,11 @@ # contrib/pageinspect/Makefile MODULE_big = pageinspect -OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o $(WIN32RES) +OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o brinfuncs.o $(WIN32RES) EXTENSION = pageinspect -DATA = pageinspect--1.2.sql pageinspect--1.0--1.1.sql \ +DATA = pageinspect--1.3.sql pageinspect--1.0--1.1.sql \ + pageinspect--1.2--1.3.sql \ pageinspect--1.1--1.2.sql pageinspect--unpackaged--1.0.sql PGFILEDESC = "pageinspect - functions to inspect contents of database pages" diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c new file mode 100644 index 00000000000..359fc1d3ac6 --- /dev/null +++ b/contrib/pageinspect/brinfuncs.c @@ -0,0 +1,414 @@ +/* + * brinfuncs.c + * Functions to investigate BRIN indexes + * + * Copyright (c) 2014, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/pageinspect/brinfuncs.c + */ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/brin.h" +#include "access/brin_internal.h" +#include "access/brin_page.h" +#include "access/brin_revmap.h" +#include "access/brin_tuple.h" +#include "catalog/index.h" +#include "catalog/pg_type.h" +#include "funcapi.h" +#include "lib/stringinfo.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "miscadmin.h" + + +PG_FUNCTION_INFO_V1(brin_page_type); +PG_FUNCTION_INFO_V1(brin_page_items); +PG_FUNCTION_INFO_V1(brin_metapage_info); +PG_FUNCTION_INFO_V1(brin_revmap_data); + +typedef struct brin_column_state +{ + int nstored; + FmgrInfo outputFn[FLEXIBLE_ARRAY_MEMBER]; +} brin_column_state; + +typedef struct brin_page_state +{ + BrinDesc *bdesc; + Page page; + OffsetNumber offset; + bool unusedItem; + bool done; + AttrNumber attno; + BrinMemTuple *dtup; + brin_column_state *columns[FLEXIBLE_ARRAY_MEMBER]; +} brin_page_state; + + +static Page verify_brin_page(bytea *raw_page, uint16 type, + const char *strtype); + +Datum +brin_page_type(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + Page page = VARDATA(raw_page); + BrinSpecialSpace *special; + char *type; + + special = (BrinSpecialSpace *) PageGetSpecialPointer(page); + + switch (special->type) + { + case BRIN_PAGETYPE_META: + type = "meta"; + break; + case BRIN_PAGETYPE_REVMAP: + type = "revmap"; + break; + case BRIN_PAGETYPE_REGULAR: + type = "regular"; + break; + default: + type = psprintf("unknown (%02x)", special->type); + break; + } + + PG_RETURN_TEXT_P(cstring_to_text(type)); +} + +/* + * Verify that the given bytea contains a BRIN page of the indicated page + * type, or die in the attempt. A pointer to the page is returned. + */ +static Page +verify_brin_page(bytea *raw_page, uint16 type, const char *strtype) +{ + Page page; + int raw_page_size; + BrinSpecialSpace *special; + + raw_page_size = VARSIZE(raw_page) - VARHDRSZ; + + if (raw_page_size < SizeOfPageHeaderData) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page too small"), + errdetail("Expected size %d, got %d", raw_page_size, BLCKSZ))); + + page = VARDATA(raw_page); + + /* verify the special space says this page is what we want */ + special = (BrinSpecialSpace *) PageGetSpecialPointer(page); + if (special->type != type) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("page is not a BRIN page of type \"%s\"", strtype), + errdetail("Expected special type %08x, got %08x.", + type, special->type))); + + return page; +} + + +/* + * Extract all item values from a BRIN index page + * + * Usage: SELECT * FROM brin_page_items(get_raw_page('idx', 1), 'idx'::regclass); + */ +Datum +brin_page_items(PG_FUNCTION_ARGS) +{ + brin_page_state *state; + FuncCallContext *fctx; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + if (SRF_IS_FIRSTCALL()) + { + bytea *raw_page = PG_GETARG_BYTEA_P(0); + Oid indexRelid = PG_GETARG_OID(1); + Page page; + TupleDesc tupdesc; + MemoryContext mctx; + Relation indexRel; + AttrNumber attno; + + /* minimally verify the page we got */ + page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular"); + + /* create a function context for cross-call persistence */ + fctx = SRF_FIRSTCALL_INIT(); + + /* switch to memory context appropriate for multiple function calls */ + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + /* 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"); + + indexRel = index_open(indexRelid, AccessShareLock); + + state = palloc(offsetof(brin_page_state, columns) + + sizeof(brin_column_state) * RelationGetDescr(indexRel)->natts); + + state->bdesc = brin_build_desc(indexRel); + state->page = page; + state->offset = FirstOffsetNumber; + state->unusedItem = false; + state->done = false; + state->dtup = NULL; + + /* + * Initialize output functions for all indexed datatypes; simplifies + * calling them later. + */ + for (attno = 1; attno <= state->bdesc->bd_tupdesc->natts; attno++) + { + Oid output; + bool isVarlena; + BrinOpcInfo *opcinfo; + int i; + brin_column_state *column; + + opcinfo = state->bdesc->bd_info[attno - 1]; + column = palloc(offsetof(brin_column_state, outputFn) + + sizeof(FmgrInfo) * opcinfo->oi_nstored); + + column->nstored = opcinfo->oi_nstored; + for (i = 0; i < opcinfo->oi_nstored; i++) + { + getTypeOutputInfo(opcinfo->oi_typids[i], &output, &isVarlena); + fmgr_info(output, &column->outputFn[i]); + } + + state->columns[attno - 1] = column; + } + + index_close(indexRel, AccessShareLock); + + fctx->user_fctx = state; + fctx->tuple_desc = BlessTupleDesc(tupdesc); + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + state = fctx->user_fctx; + + if (!state->done) + { + HeapTuple result; + Datum values[7]; + bool nulls[7]; + + /* + * This loop is called once for every attribute of every tuple in the + * page. At the start of a tuple, we get a NULL dtup; that's our + * signal for obtaining and decoding the next one. If that's not the + * case, we output the next attribute. + */ + if (state->dtup == NULL) + { + BrinTuple *tup; + MemoryContext mctx; + ItemId itemId; + + /* deformed tuple must live across calls */ + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + /* verify item status: if there's no data, we can't decode */ + itemId = PageGetItemId(state->page, state->offset); + if (ItemIdIsUsed(itemId)) + { + tup = (BrinTuple *) PageGetItem(state->page, + PageGetItemId(state->page, + state->offset)); + state->dtup = brin_deform_tuple(state->bdesc, tup); + state->attno = 1; + state->unusedItem = false; + } + else + state->unusedItem = true; + + MemoryContextSwitchTo(mctx); + } + else + state->attno++; + + MemSet(nulls, 0, sizeof(nulls)); + + if (state->unusedItem) + { + values[0] = UInt16GetDatum(state->offset); + nulls[1] = true; + nulls[2] = true; + nulls[3] = true; + nulls[4] = true; + nulls[5] = true; + nulls[6] = true; + } + else + { + int att = state->attno - 1; + + values[0] = UInt16GetDatum(state->offset); + values[1] = UInt32GetDatum(state->dtup->bt_blkno); + values[2] = UInt16GetDatum(state->attno); + values[3] = BoolGetDatum(state->dtup->bt_columns[att].bv_allnulls); + values[4] = BoolGetDatum(state->dtup->bt_columns[att].bv_hasnulls); + values[5] = BoolGetDatum(state->dtup->bt_placeholder); + if (!state->dtup->bt_columns[att].bv_allnulls) + { + BrinValues *bvalues = &state->dtup->bt_columns[att]; + StringInfoData s; + bool first; + int i; + + initStringInfo(&s); + appendStringInfoChar(&s, '{'); + + first = true; + for (i = 0; i < state->columns[att]->nstored; i++) + { + char *val; + + if (!first) + appendStringInfoString(&s, " .. "); + first = false; + val = OutputFunctionCall(&state->columns[att]->outputFn[i], + bvalues->bv_values[i]); + appendStringInfoString(&s, val); + pfree(val); + } + appendStringInfoChar(&s, '}'); + + values[6] = CStringGetTextDatum(s.data); + pfree(s.data); + } + else + { + nulls[6] = true; + } + } + + result = heap_form_tuple(fctx->tuple_desc, values, nulls); + + /* + * If the item was unused, jump straight to the next one; otherwise, + * the only cleanup needed here is to set our signal to go to the next + * tuple in the following iteration, by freeing the current one. + */ + if (state->unusedItem) + state->offset = OffsetNumberNext(state->offset); + else if (state->attno >= state->bdesc->bd_tupdesc->natts) + { + pfree(state->dtup); + state->dtup = NULL; + state->offset = OffsetNumberNext(state->offset); + } + + /* + * If we're beyond the end of the page, set flag to end the function in + * the following iteration. + */ + if (state->offset > PageGetMaxOffsetNumber(state->page)) + state->done = true; + + SRF_RETURN_NEXT(fctx, HeapTupleGetDatum(result)); + } + + brin_free_desc(state->bdesc); + + SRF_RETURN_DONE(fctx); +} + +Datum +brin_metapage_info(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + Page page; + BrinMetaPageData *meta; + TupleDesc tupdesc; + Datum values[4]; + bool nulls[4]; + HeapTuple htup; + + page = verify_brin_page(raw_page, BRIN_PAGETYPE_META, "metapage"); + + /* 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"); + tupdesc = BlessTupleDesc(tupdesc); + + /* Extract values from the metapage */ + meta = (BrinMetaPageData *) PageGetContents(page); + MemSet(nulls, 0, sizeof(nulls)); + values[0] = CStringGetTextDatum(psprintf("0x%08X", meta->brinMagic)); + values[1] = Int32GetDatum(meta->brinVersion); + values[2] = Int32GetDatum(meta->pagesPerRange); + values[3] = Int64GetDatum(meta->lastRevmapPage); + + htup = heap_form_tuple(tupdesc, values, nulls); + + PG_RETURN_DATUM(HeapTupleGetDatum(htup)); +} + +/* + * Return the TID array stored in a BRIN revmap page + */ +Datum +brin_revmap_data(PG_FUNCTION_ARGS) +{ + struct + { + ItemPointerData *tids; + int idx; + } *state; + FuncCallContext *fctx; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + if (SRF_IS_FIRSTCALL()) + { + bytea *raw_page = PG_GETARG_BYTEA_P(0); + MemoryContext mctx; + Page page; + + /* minimally verify the page we got */ + page = verify_brin_page(raw_page, BRIN_PAGETYPE_REVMAP, "revmap"); + + /* create a function context for cross-call persistence */ + fctx = SRF_FIRSTCALL_INIT(); + + /* switch to memory context appropriate for multiple function calls */ + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + state = palloc(sizeof(*state)); + state->tids = ((RevmapContents *) PageGetContents(page))->rm_tids; + state->idx = 0; + + fctx->user_fctx = state; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + state = fctx->user_fctx; + + if (state->idx < REVMAP_PAGE_MAXITEMS) + SRF_RETURN_NEXT(fctx, PointerGetDatum(&state->tids[state->idx++])); + + SRF_RETURN_DONE(fctx); +} diff --git a/contrib/pageinspect/pageinspect--1.2--1.3.sql b/contrib/pageinspect/pageinspect--1.2--1.3.sql new file mode 100644 index 00000000000..9bc4dded0f4 --- /dev/null +++ b/contrib/pageinspect/pageinspect--1.2--1.3.sql @@ -0,0 +1,43 @@ +/* contrib/pageinspect/pageinspect--1.2--1.3.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.3'" to load this file. \quit + +-- +-- brin_page_type() +-- +CREATE FUNCTION brin_page_type(IN page bytea) +RETURNS text +AS 'MODULE_PATHNAME', 'brin_page_type' +LANGUAGE C STRICT; + +-- +-- brin_metapage_info() +-- +CREATE FUNCTION brin_metapage_info(IN page bytea, OUT magic text, + OUT version integer, OUT pagesperrange integer, OUT lastrevmappage bigint) +AS 'MODULE_PATHNAME', 'brin_metapage_info' +LANGUAGE C STRICT; + +-- +-- brin_revmap_data() +CREATE FUNCTION brin_revmap_data(IN page bytea, + OUT pages tid) +RETURNS SETOF tid +AS 'MODULE_PATHNAME', 'brin_revmap_data' +LANGUAGE C STRICT; + +-- +-- brin_page_items() +-- +CREATE FUNCTION brin_page_items(IN page bytea, IN index_oid regclass, + OUT itemoffset int, + OUT blknum int, + OUT attnum int, + OUT allnulls bool, + OUT hasnulls bool, + OUT placeholder bool, + OUT value text) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'brin_page_items' +LANGUAGE C STRICT; diff --git a/contrib/pageinspect/pageinspect--1.2.sql b/contrib/pageinspect/pageinspect--1.3.sql index 15e8e1e3811..856dcdfb592 100644 --- a/contrib/pageinspect/pageinspect--1.2.sql +++ b/contrib/pageinspect/pageinspect--1.3.sql @@ -1,4 +1,4 @@ -/* contrib/pageinspect/pageinspect--1.2.sql */ +/* contrib/pageinspect/pageinspect--1.3.sql */ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pageinspect" to load this file. \quit @@ -99,6 +99,45 @@ AS 'MODULE_PATHNAME', 'bt_page_items' LANGUAGE C STRICT; -- +-- brin_page_type() +-- +CREATE FUNCTION brin_page_type(IN page bytea) +RETURNS text +AS 'MODULE_PATHNAME', 'brin_page_type' +LANGUAGE C STRICT; + +-- +-- brin_metapage_info() +-- +CREATE FUNCTION brin_metapage_info(IN page bytea, OUT magic text, + OUT version integer, OUT pagesperrange integer, OUT lastrevmappage bigint) +AS 'MODULE_PATHNAME', 'brin_metapage_info' +LANGUAGE C STRICT; + +-- +-- brin_revmap_data() +CREATE FUNCTION brin_revmap_data(IN page bytea, + OUT pages tid) +RETURNS SETOF tid +AS 'MODULE_PATHNAME', 'brin_revmap_data' +LANGUAGE C STRICT; + +-- +-- brin_page_items() +-- +CREATE FUNCTION brin_page_items(IN page bytea, IN index_oid regclass, + OUT itemoffset int, + OUT blknum int, + OUT attnum int, + OUT allnulls bool, + OUT hasnulls bool, + OUT placeholder bool, + OUT value text) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'brin_page_items' +LANGUAGE C STRICT; + +-- -- fsm_page_contents() -- CREATE FUNCTION fsm_page_contents(IN page bytea) diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control index aecd91a711b..a9dab3327c9 100644 --- a/contrib/pageinspect/pageinspect.control +++ b/contrib/pageinspect/pageinspect.control @@ -1,5 +1,5 @@ # pageinspect extension comment = 'inspect the contents of database pages at a low level' -default_version = '1.2' +default_version = '1.3' module_pathname = '$libdir/pageinspect' relocatable = true diff --git a/contrib/pg_xlogdump/rmgrdesc.c b/contrib/pg_xlogdump/rmgrdesc.c index bfb35738789..93971982390 100644 --- a/contrib/pg_xlogdump/rmgrdesc.c +++ b/contrib/pg_xlogdump/rmgrdesc.c @@ -8,6 +8,7 @@ #define FRONTEND 1 #include "postgres.h" +#include "access/brin_xlog.h" #include "access/clog.h" #include "access/gin.h" #include "access/gist_private.h" |