Attached v3 again, for CFbot's benefit. No changes from last time. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>From 018077d786f874cb314b5f61b5ef85f42c62bbe5 Mon Sep 17 00:00:00 2001 From: Masahiko Sawada <sawada.m...@gmail.com> Date: Fri, 23 Aug 2019 10:36:13 +0900 Subject: [PATCH v3] Introduce heap_infomask_flags to decode infomask and infomask2
--- contrib/pageinspect/Makefile | 2 +- contrib/pageinspect/expected/page.out | 97 ++++++++++++++++++++++++ contrib/pageinspect/heapfuncs.c | 104 ++++++++++++++++++++++++++ contrib/pageinspect/pageinspect--1.7--1.8.sql | 13 ++++ contrib/pageinspect/pageinspect.control | 2 +- contrib/pageinspect/sql/page.sql | 26 +++++++ doc/src/sgml/pageinspect.sgml | 33 ++++++++ 7 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 contrib/pageinspect/pageinspect--1.7--1.8.sql diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index e5a581f..cfe0129 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -5,7 +5,7 @@ OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o \ brinfuncs.o ginfuncs.o hashfuncs.o $(WIN32RES) EXTENSION = pageinspect -DATA = pageinspect--1.6--1.7.sql \ +DATA = pageinspect--1.7--1.8.sql 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 \ diff --git a/contrib/pageinspect/expected/page.out b/contrib/pageinspect/expected/page.out index 3fcd9fb..3b47599 100644 --- a/contrib/pageinspect/expected/page.out +++ b/contrib/pageinspect/expected/page.out @@ -82,6 +82,103 @@ 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_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_XMAX_INVALID,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID} +(1 row) + +SELECT heap_infomask_flags(2816, 0); -- show raw flags by default + heap_infomask_flags +----------------------------------------------------------- + {HEAP_XMAX_INVALID,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID} +(1 row) + +SELECT heap_infomask_flags(2816, 0, true); + heap_infomask_flags +-------------------------------------- + {HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN} +(1 row) + +SELECT heap_infomask_flags(2816, 1, true); + heap_infomask_flags +-------------------------------------- + {HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN} +(1 row) + +SELECT heap_infomask_flags(2816, 1, false); + heap_infomask_flags +----------------------------------------------------------- + {HEAP_XMAX_INVALID,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID} +(1 row) + +SELECT heap_infomask_flags(2816, x'FFFF'::integer, true); + heap_infomask_flags +----------------------------------------------------------------------------------------- + {HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN,HEAP_KEYS_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE} +(1 row) + +SELECT heap_infomask_flags(2816, x'FFFF'::integer, false); + heap_infomask_flags +-------------------------------------------------------------------------------------------------------------- + {HEAP_XMAX_INVALID,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_KEYS_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE} +(1 row) + +SELECT heap_infomask_flags(x'1080'::integer, 0, true); -- test for HEAP_LOCKED_UPGRADED + heap_infomask_flags +------------------------ + {HEAP_LOCKED_UPGRADED} +(1 row) + +SELECT heap_infomask_flags(x'1080'::integer, 0, false); + heap_infomask_flags +------------------------------------------ + {HEAP_XMAX_LOCK_ONLY,HEAP_XMAX_IS_MULTI} +(1 row) + +SELECT heap_infomask_flags(x'FFFF'::integer, x'FFFF'::integer, false); + heap_infomask_flags +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + {HEAP_HASNULL,HEAP_HASVARWIDTH,HEAP_HASEXTERNAL,HEAP_HASOID_OLD,HEAP_COMBOCID,HEAP_XMAX_COMMITTED,HEAP_XMAX_INVALID,HEAP_UPDATED,HEAP_XMAX_EXCL_LOCK,HEAP_XMAX_KEYSHR_LOCK,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_MOVED_IN,HEAP_MOVED_OFF,HEAP_XMAX_LOCK_ONLY,HEAP_XMAX_IS_MULTI,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_OLD,HEAP_COMBOCID,HEAP_XMAX_COMMITTED,HEAP_XMAX_INVALID,HEAP_UPDATED,HEAP_XMAX_SHR_LOCK,HEAP_XMIN_FROZEN,HEAP_MOVED,HEAP_XMAX_LOCK_ONLY,HEAP_XMAX_IS_MULTI,HEAP_KEYS_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE} +(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_OLD,HEAP_COMBOCID,HEAP_XMAX_COMMITTED,HEAP_XMAX_INVALID,HEAP_UPDATED,HEAP_XMAX_EXCL_LOCK,HEAP_XMAX_KEYSHR_LOCK,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_MOVED_IN,HEAP_MOVED_OFF,HEAP_XMAX_LOCK_ONLY,HEAP_XMAX_IS_MULTI,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 or index -- would fail diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c index 64a6e35..0a89d47 100644 --- a/contrib/pageinspect/heapfuncs.c +++ b/contrib/pageinspect/heapfuncs.c @@ -33,6 +33,7 @@ #include "catalog/pg_am_d.h" #include "catalog/pg_type.h" #include "miscadmin.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/rel.h" @@ -494,3 +495,106 @@ tuple_data_split(PG_FUNCTION_ARGS) PG_RETURN_ARRAYTYPE_P(res); } + +/* + * Decode an infomask, per htup_details.c, into human readable + * form. For detail of masks see access/htup_details.h. + */ +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 decode_combined = PG_GETARG_BOOL(2); + int cnt = 0; + ArrayType *a; + int bitcnt; + Datum *d; + + bitcnt = pg_popcount32(t_infomask) + pg_popcount32(t_infomask2); + if (bitcnt == 0) + { + /* If no flags, return an empty array */ + a = construct_empty_array(TEXTOID); + PG_RETURN_POINTER(a); + } + + d = (Datum *) palloc0(sizeof(Datum) * bitcnt); + + /* decode t_infomask */ + if ((t_infomask & HEAP_HASNULL) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_HASNULL"); + if ((t_infomask & HEAP_HASVARWIDTH) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_HASVARWIDTH"); + if ((t_infomask & HEAP_HASEXTERNAL) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_HASEXTERNAL"); + if ((t_infomask & HEAP_HASOID_OLD) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_HASOID_OLD"); + if ((t_infomask & HEAP_COMBOCID) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_COMBOCID"); + if ((t_infomask & HEAP_XMAX_COMMITTED) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_XMAX_COMMITTED"); + if ((t_infomask & HEAP_XMAX_INVALID) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_XMAX_INVALID"); + if ((t_infomask & HEAP_UPDATED) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_UPDATED"); + + /* decode combined masks of t_infomaks */ + if (decode_combined && + (t_infomask & HEAP_XMAX_SHR_LOCK) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_XMAX_SHR_LOCK"); + else + { + if ((t_infomask & HEAP_XMAX_EXCL_LOCK) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_XMAX_EXCL_LOCK"); + if ((t_infomask & HEAP_XMAX_KEYSHR_LOCK) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_XMAX_KEYSHR_LOCK"); + } + + if (decode_combined && + (t_infomask & HEAP_XMIN_FROZEN) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_XMIN_FROZEN"); + else + { + if ((t_infomask & HEAP_XMIN_COMMITTED) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_XMIN_COMMITTED"); + if ((t_infomask & HEAP_XMIN_INVALID) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_XMIN_INVALID"); + } + + if (decode_combined && + (t_infomask & HEAP_MOVED) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_MOVED"); + else + { + if ((t_infomask & HEAP_MOVED_IN) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_MOVED_IN"); + if ((t_infomask & HEAP_MOVED_OFF) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_MOVED_OFF"); + } + + if (decode_combined && + HEAP_LOCKED_UPGRADED(t_infomask)) + d[cnt++] = CStringGetTextDatum("HEAP_LOCKED_UPGRADED"); + else + { + if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask)) + d[cnt++] = CStringGetTextDatum("HEAP_XMAX_LOCK_ONLY"); + if ((t_infomask & HEAP_XMAX_IS_MULTI) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_XMAX_IS_MULTI"); + } + + /* decode t_infomask2 */ + if ((t_infomask2 & HEAP_KEYS_UPDATED) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_KEYS_UPDATED"); + if ((t_infomask2 & HEAP_HOT_UPDATED) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_HOT_UPDATED"); + if ((t_infomask2 & HEAP_ONLY_TUPLE) != 0) + d[cnt++] = CStringGetTextDatum("HEAP_ONLY_TUPLE"); + + a = construct_array(d, cnt, TEXTOID, -1, false, 'i'); + + PG_RETURN_POINTER(a); +} diff --git a/contrib/pageinspect/pageinspect--1.7--1.8.sql b/contrib/pageinspect/pageinspect--1.7--1.8.sql new file mode 100644 index 0000000..aaf9399 --- /dev/null +++ b/contrib/pageinspect/pageinspect--1.7--1.8.sql @@ -0,0 +1,13 @@ +/* contrib/pageinspect/pageinspect--1.7--1.8.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.8'" to load this file. \quit + +-- decode infomask flags as human readable flag names +CREATE FUNCTION heap_infomask_flags( + infomask integer, + infomask2 integer, + decode_combined boolean DEFAULT false) +RETURNS text[] +AS 'MODULE_PATHNAME', 'heap_infomask_flags' +LANGUAGE C STRICT PARALLEL SAFE; diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control index dcfc61f..f8cdf52 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.7' +default_version = '1.8' module_pathname = '$libdir/pageinspect' relocatable = true diff --git a/contrib/pageinspect/sql/page.sql b/contrib/pageinspect/sql/page.sql index 8ac9991..4ac96ef 100644 --- a/contrib/pageinspect/sql/page.sql +++ b/contrib/pageinspect/sql/page.sql @@ -31,6 +31,32 @@ 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); -- show raw flags by default +SELECT heap_infomask_flags(2816, 0, true); +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'1080'::integer, 0, true); -- test for HEAP_LOCKED_UPGRADED +SELECT heap_infomask_flags(x'1080'::integer, 0, 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 or index diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml index 0b92025..258a0f1 100644 --- a/doc/src/sgml/pageinspect.sgml +++ b/doc/src/sgml/pageinspect.sgml @@ -180,6 +180,10 @@ test=# SELECT * FROM heap_page_items(get_raw_page('pg_class', 0)); <filename>src/include/access/htup_details.h</filename> 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> @@ -232,6 +236,35 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class </para> </listitem> </varlistentry> + + <varlistentry> + <term> + <function>heap_infomask_flags(infomask integer, infomask2 integer, decode_combined bool) returns text[]</function> + <indexterm> + <primary>heap_infomask_flags</primary> + </indexterm> + </term> + <listitem> + <para> + <function>heap_infomask_flags</function> decodes the + <structfield>t_infomask</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 decode_combined is set, combination flags like + <literal>HEAP_XMIN_FROZEN</literal> are output instead of raw + flags, <literal>HEAP_XMIN_COMMITTED</literal> and + <literal>HEAP_XMIN_INVALID</literal>. Default value is + <literal>false</literal>. + </para> + <para> + For the meaning of these flags see + <filename>src/include/access/htup_details.h</filename> + </para> + </listitem> + </varlistentry> </variablelist> </sect2> -- 2.10.5