On 20 July 2017 at 19:09, Ashutosh Sharma <ashu.coe...@gmail.com> wrote:
> I had a quick look into this patch and also tested it and following > are my observations. > > 1) I am seeing a server crash when passing any non meaningful value > for t_infomask2 to heap_infomask_flags(). > > postgres=# SELECT heap_infomask_flags(2816, 3); > server closed the connection unexpectedly > This probably means the server terminated abnormally > before or while processing the request. > The connection to the server was lost. Attempting reset: Failed. > !> \q > > Following is the backtrace, > > (gdb) bt > #0 0x0000000000d9c55b in pg_detoast_datum (datum=0x0) at fmgr.c:1833 > #1 0x0000000000b87374 in construct_md_array (elems=0x2ad74c0, > nulls=0x0, ndims=1, dims=0x7ffc0b0bbcd0, lbs=0x7ffc0b0bbcc0, > elmtype=25, elmlen=-1, > elmbyval=0 '\000', elmalign=105 'i') at arrayfuncs.c:3382 > #2 0x0000000000b8709f in construct_array (elems=0x2ad74c0, nelems=10, > elmtype=25, elmlen=-1, elmbyval=0 '\000', elmalign=105 'i') at > arrayfuncs.c:3316 > #3 0x00007fb8001603a5 in heap_infomask_flags (fcinfo=0x2ad3b88) at > heapfuncs.c:597 > Fixed. > 2) I can see the documentation for heap_infomask(). But, I do not see > it being defined or used anywhere in the patch. > > + <para> > + The <function>heap_infomask</function> function can be used to > unpack the > + recognised bits of the infomasks of heap tuples. > + </para> > Fixed. Renamed the function, missed a spot. > 3) If show_combined flag is set to it's default value and a tuple is > frozen then may i know the reason for not showing it as frozen tuple > when t_infomask2 > is passed as zero. It was a consequence of (1). Fixed. > 4) I think, it would be better to use the same argument name for the > newly added function i.e heap_infomask_flags() in both documentation > and sql file. I am basically refering to 'include_combined' argument. > IF you see the function definition, the argument name used is > 'include_combined' whereas in documentation you have mentioned > 'show_combined'. > Fixed, thanks. I want to find time to expand the tests on this some more and look more closely, but here it is for now. -- Craig Ringer http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services
From a25c1e50e3b3ff493a018ab594a71dc3d64832ee Mon Sep 17 00:00:00 2001 From: Craig Ringer <cr...@2ndquadrant.com> Date: Thu, 20 Jul 2017 11:20:21 +0800 Subject: [PATCH v2] Introduce heap_infomask_flags to decode infomask and infomask2 --- contrib/pageinspect/Makefile | 3 +- contrib/pageinspect/expected/page.out | 85 ++++++++++++++++++ contrib/pageinspect/heapfuncs.c | 120 ++++++++++++++++++++++++++ contrib/pageinspect/pageinspect--1.6--1.7.sql | 9 ++ contrib/pageinspect/pageinspect.control | 2 +- contrib/pageinspect/sql/page.sql | 24 ++++++ doc/src/sgml/pageinspect.sgml | 32 +++++++ 7 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 contrib/pageinspect/pageinspect--1.6--1.7.sql diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index 0a3cbee..de114c7 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -5,7 +5,8 @@ OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o \ brinfuncs.o ginfuncs.o hashfuncs.o $(WIN32RES) EXTENSION = pageinspect -DATA = pageinspect--1.5.sql pageinspect--1.5--1.6.sql \ +DATA = pageinspect--1.6--1.7.sql \ + pageinspect--1.5.sql pageinspect--1.5--1.6.sql \ pageinspect--1.4--1.5.sql pageinspect--1.3--1.4.sql \ pageinspect--1.2--1.3.sql pageinspect--1.1--1.2.sql \ pageinspect--1.0--1.1.sql pageinspect--unpackaged--1.0.sql diff --git a/contrib/pageinspect/expected/page.out b/contrib/pageinspect/expected/page.out index 8e15947..b335265 100644 --- a/contrib/pageinspect/expected/page.out +++ b/contrib/pageinspect/expected/page.out @@ -82,6 +82,91 @@ SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0)); (1 row) +-- If we freeze the only tuple on test1, the infomask should +-- always be the same in all test runs. +VACUUM FREEZE test1; +SELECT t_infomask, t_infomask2, flags +FROM heap_page_items(get_raw_page('test1', 0)), + LATERAL heap_infomask_flags(t_infomask, t_infomask2, true) m(flags); + t_infomask | t_infomask2 | flags +------------+-------------+---------------------------------------------------------------------------- + 2816 | 2 | {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN} +(1 row) + +SELECT t_infomask, t_infomask2, flags +FROM heap_page_items(get_raw_page('test1', 0)), + LATERAL heap_infomask_flags(t_infomask, t_infomask2, false) m(flags); + t_infomask | t_infomask2 | flags +------------+-------------+----------------------------------------------------------- + 2816 | 2 | {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID} +(1 row) + +SELECT heap_infomask_flags(2816, 0); + heap_infomask_flags +---------------------------------------------------------------------------- + {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN} +(1 row) + +SELECT heap_infomask_flags(2816, 0, false); + heap_infomask_flags +----------------------------------------------------------- + {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID} +(1 row) + +SELECT heap_infomask_flags(2816, 1, true); + heap_infomask_flags +---------------------------------------------------------------------------- + {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN} +(1 row) + +SELECT heap_infomask_flags(2816, 1, false); + heap_infomask_flags +----------------------------------------------------------- + {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID} +(1 row) + +SELECT heap_infomask_flags(2816, x'FFFF'::integer, true); + heap_infomask_flags +------------------------------------------------------------------------------------------------------------------------------- + {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID,HEAP_KEYS_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE,HEAP_XMIN_FROZEN} +(1 row) + +SELECT heap_infomask_flags(2816, x'FFFF'::integer, false); + heap_infomask_flags +-------------------------------------------------------------------------------------------------------------- + {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID,HEAP_KEYS_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE} +(1 row) + +SELECT heap_infomask_flags(x'FFFF'::integer, x'FFFF'::integer, false); + heap_infomask_flags +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {HEAP_HASNULL,HEAP_HASVARWIDTH,HEAP_HASEXTERNAL,HEAP_HASOID,HEAP_XMAX_KEYSHR_LOCK,HEAP_COMBOCID,HEAP_XMAX_EXCL_LOCK,HEAP_XMAX_LOCK_ONLY,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_COMMITTED,HEAP_XMAX_INVALID,HEAP_XMAX_IS_MULTI,HEAP_UPDATED,HEAP_MOVED_OFF,HEAP_MOVED_IN,HEAP_KEYS_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE} +(1 row) + +SELECT heap_infomask_flags(x'FFFF'::integer, x'FFFF'::integer, true); + heap_infomask_flags +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {HEAP_HASNULL,HEAP_HASVARWIDTH,HEAP_HASEXTERNAL,HEAP_HASOID,HEAP_XMAX_KEYSHR_LOCK,HEAP_COMBOCID,HEAP_XMAX_EXCL_LOCK,HEAP_XMAX_LOCK_ONLY,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_COMMITTED,HEAP_XMAX_INVALID,HEAP_XMAX_IS_MULTI,HEAP_UPDATED,HEAP_MOVED_OFF,HEAP_MOVED_IN,HEAP_KEYS_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE,HEAP_XMIN_FROZEN,HEAP_XMAX_IS_LOCKED_ONLY} +(1 row) + +SELECT heap_infomask_flags(0, 0, true); + heap_infomask_flags +--------------------- + {} +(1 row) + +SELECT heap_infomask_flags(0, 0, false); + heap_infomask_flags +--------------------- + {} +(1 row) + +SELECT heap_infomask_flags(-1, -1, false); + heap_infomask_flags +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {HEAP_HASNULL,HEAP_HASVARWIDTH,HEAP_HASEXTERNAL,HEAP_HASOID,HEAP_XMAX_KEYSHR_LOCK,HEAP_COMBOCID,HEAP_XMAX_EXCL_LOCK,HEAP_XMAX_LOCK_ONLY,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_COMMITTED,HEAP_XMAX_INVALID,HEAP_XMAX_IS_MULTI,HEAP_UPDATED,HEAP_MOVED_OFF,HEAP_MOVED_IN,HEAP_KEYS_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE} +(1 row) + DROP TABLE test1; -- check that using any of these functions with a partitioned table would fail create table test_partitioned (a int) partition by range (a); diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c index 72d1776..2ef5b39 100644 --- a/contrib/pageinspect/heapfuncs.c +++ b/contrib/pageinspect/heapfuncs.c @@ -478,3 +478,123 @@ tuple_data_split(PG_FUNCTION_ARGS) PG_RETURN_ARRAYTYPE_P(res); } + +/* + * Brian Kernighan's popcount algorithm for counting number of set bits in a + * mask. The faster methods aren't worth the complexity here. + * + * See e.g. http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan + * + * gcc has __builtin_popcount but there's no standard alternative. + */ +static inline int +pg_popcount32(uint32 mask) +{ + unsigned int c; // c accumulates the total bits set in v + for (c = 0; mask; c++) + { + mask &= mask - 1; // clear the least significant bit set + } + return c; +} + +/* + * Infomask flag names. The infomask2 values are shifted into the + * high 16 bits. + */ +struct infomask_details +{ + uint32 flag_value; + const char flag_name[24]; +}; + +#define MASKINFO_LENGTH 19 +static struct infomask_details maskinfo[MASKINFO_LENGTH] = + { + /* infomask1 values */ + {(uint32)HEAP_HASNULL, "HEAP_HASNULL"}, + {(uint32)HEAP_HASVARWIDTH, "HEAP_HASVARWIDTH"}, + {(uint32)HEAP_HASEXTERNAL, "HEAP_HASEXTERNAL"}, + {(uint32)HEAP_HASOID, "HEAP_HASOID"}, + {(uint32)HEAP_XMAX_KEYSHR_LOCK, "HEAP_XMAX_KEYSHR_LOCK"}, + {(uint32)HEAP_COMBOCID, "HEAP_COMBOCID"}, + {(uint32)HEAP_XMAX_EXCL_LOCK, "HEAP_XMAX_EXCL_LOCK"}, + {(uint32)HEAP_XMAX_LOCK_ONLY, "HEAP_XMAX_LOCK_ONLY"}, + {(uint32)HEAP_XMIN_COMMITTED, "HEAP_XMIN_COMMITTED"}, + {(uint32)HEAP_XMIN_INVALID, "HEAP_XMIN_INVALID"}, + {(uint32)HEAP_XMAX_COMMITTED, "HEAP_XMAX_COMMITTED"}, + {(uint32)HEAP_XMAX_INVALID, "HEAP_XMAX_INVALID"}, + {(uint32)HEAP_XMAX_IS_MULTI, "HEAP_XMAX_IS_MULTI"}, + {(uint32)HEAP_UPDATED, "HEAP_UPDATED"}, + {(uint32)HEAP_MOVED_OFF, "HEAP_MOVED_OFF"}, + {(uint32)HEAP_MOVED_IN, "HEAP_MOVED_IN"}, + /* infomask2 values */ + {((uint32)HEAP_KEYS_UPDATED)<<16, "HEAP_KEYS_UPDATED"}, + {((uint32)HEAP_HOT_UPDATED)<<16, "HEAP_HOT_UPDATED"}, + {((uint32)HEAP_ONLY_TUPLE)<<16, "HEAP_ONLY_TUPLE"}, + }; + +/* + * Decode an infomask, per htup_details.c, into human readable + * form. + */ +PG_FUNCTION_INFO_V1(heap_infomask_flags); + +Datum +heap_infomask_flags(PG_FUNCTION_ARGS) +{ + uint16 t_infomask = PG_GETARG_INT16(0); + uint16 t_infomask2 = PG_GETARG_INT16(1); + bool include_combined = PG_GETARG_BOOL(2); + uint32 combomask = (((uint32)t_infomask2) << 16) + (uint32)t_infomask; + unsigned int maxarray = pg_popcount32(combomask); + Datum *d; + ArrayType *a; + int i; + int insertpos; + + Assert((t_infomask == 0 && t_infomask2 == 0) == (maxarray == 0)); + + if (maxarray == 0) + { + a = construct_empty_array(TEXTOID); + PG_RETURN_POINTER(a); + } + + if (include_combined) + { + maxarray += (t_infomask & HEAP_XMIN_FROZEN) == HEAP_XMIN_FROZEN; + maxarray += HEAP_XMAX_IS_LOCKED_ONLY(t_infomask) != 0; + maxarray += HEAP_LOCKED_UPGRADED(t_infomask) != 0; + } + + d = (Datum *) palloc0(sizeof(Datum) * maxarray); + + insertpos = 0; + for (i = 0; i < MASKINFO_LENGTH; ++i) + { + if ((combomask & maskinfo[i].flag_value) == maskinfo[i].flag_value) + d[insertpos++] = CStringGetTextDatum(maskinfo[i].flag_name); + } + + /* + * These tests are useful to report in the mask we output, since they're + * much more simply done here than in SQL, and here they won't get out of + * sync with what Pg does if we change it later. + */ + if (include_combined) + { + if ((t_infomask & HEAP_XMIN_FROZEN) == HEAP_XMIN_FROZEN) + d[insertpos++] = CStringGetTextDatum("HEAP_XMIN_FROZEN"); + + if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask)) + d[insertpos++] = CStringGetTextDatum("HEAP_XMAX_IS_LOCKED_ONLY"); + + if (HEAP_LOCKED_UPGRADED(t_infomask)) + d[insertpos++] = CStringGetTextDatum("HEAP_LOCKED_UPGRADED"); + } + + a = construct_array(d, insertpos, TEXTOID, -1, false, 'i'); + + PG_RETURN_POINTER(a); +} diff --git a/contrib/pageinspect/pageinspect--1.6--1.7.sql b/contrib/pageinspect/pageinspect--1.6--1.7.sql new file mode 100644 index 0000000..b3d1fe4 --- /dev/null +++ b/contrib/pageinspect/pageinspect--1.6--1.7.sql @@ -0,0 +1,9 @@ +/* contrib/pageinspect/pageinspect--1.6--1.7.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.7'" to load this file. \quit + +-- decode infomask flags as human readable flag names +CREATE FUNCTION heap_infomask_flags(infomask1 integer, infomask2 integer, + include_combined boolean DEFAULT true) +RETURNS text[] STRICT LANGUAGE 'c' AS 'MODULE_PATHNAME','heap_infomask_flags'; diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control index 1a61c9f..dcfc61f 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.6' +default_version = '1.7' module_pathname = '$libdir/pageinspect' relocatable = true diff --git a/contrib/pageinspect/sql/page.sql b/contrib/pageinspect/sql/page.sql index 493ca9b..2f06c39 100644 --- a/contrib/pageinspect/sql/page.sql +++ b/contrib/pageinspect/sql/page.sql @@ -31,6 +31,30 @@ SELECT tuple_data_split('test1'::regclass, t_data, t_infomask, t_infomask2, t_bi SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0)); +-- If we freeze the only tuple on test1, the infomask should +-- always be the same in all test runs. +VACUUM FREEZE test1; + +SELECT t_infomask, t_infomask2, flags +FROM heap_page_items(get_raw_page('test1', 0)), + LATERAL heap_infomask_flags(t_infomask, t_infomask2, true) m(flags); + +SELECT t_infomask, t_infomask2, flags +FROM heap_page_items(get_raw_page('test1', 0)), + LATERAL heap_infomask_flags(t_infomask, t_infomask2, false) m(flags); + +SELECT heap_infomask_flags(2816, 0); +SELECT heap_infomask_flags(2816, 0, false); +SELECT heap_infomask_flags(2816, 1, true); +SELECT heap_infomask_flags(2816, 1, false); +SELECT heap_infomask_flags(2816, x'FFFF'::integer, true); +SELECT heap_infomask_flags(2816, x'FFFF'::integer, false); +SELECT heap_infomask_flags(x'FFFF'::integer, x'FFFF'::integer, false); +SELECT heap_infomask_flags(x'FFFF'::integer, x'FFFF'::integer, true); +SELECT heap_infomask_flags(0, 0, true); +SELECT heap_infomask_flags(0, 0, false); +SELECT heap_infomask_flags(-1, -1, false); + DROP TABLE test1; -- check that using any of these functions with a partitioned table would fail diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml index ccdaf3e..52bb9d5 100644 --- a/doc/src/sgml/pageinspect.sgml +++ b/doc/src/sgml/pageinspect.sgml @@ -151,6 +151,10 @@ test=# SELECT * FROM heap_page_items(get_raw_page('pg_class', 0)); <filename>src/include/access/htup_details.h</> for explanations of the fields returned. </para> + <para> + The <function>heap_infomask_flags</function> function can be used to unpack the + recognised bits of the infomasks of heap tuples. + </para> </listitem> </varlistentry> @@ -206,6 +210,34 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class <varlistentry> <term> + <function>heap_infomask_flags(infomask1 integer, infomask2 integer, include_combined bool) returns text[]</function> + <indexterm> + <primary>heap_infomask_flags</primary> + </indexterm> + </term> + <listitem> + <para> + <function>heap_infomask_flags</function> decodes the + <structfield>t_infomask1</structfield> and + <structfield>t_infomask2</structfield> returned by + <function>heap_page_items</function> into a human-readable array of flag + names. This can be used to see the tuple hint bits etc. + </para> + <para> + If include_combined is set (the default), combination flags like + <literal>HEAP_XMIN_FROZEN</literal> are also output. The original fields + are not filtered out, so e.g. a frozen tuple will have + <literal>HEAP_XMIN_FROZEN, HEAP_XMIN_COMMITTED, HEAP_XMIN_INVALID</literal>. + </para> + <para> + For the meaning of these flags see + <filename>src/include/access/htup_details.h</> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> <function>fsm_page_contents(page bytea) returns text</function> <indexterm> <primary>fsm_page_contents</primary> -- 2.9.4
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers