On Thu, Aug 22, 2019 at 12:36 AM Masahiko Sawada <sawada.m...@gmail.com> wrote: > > On Thu, Aug 22, 2019 at 12:19 AM Alvaro Herrera > <alvhe...@2ndquadrant.com> wrote: > > > > Can I interest someone into updating this patch? We now have (I think) > > an agreed design, and I think the development work needed should be > > straightforward. We also already have the popcount stuff, so that's a > > few lines to be removed from the patch ... > > > > I will update the patch and register to the next Commit Fest tomorrow > if nobody is interested in. >
Attached the updated patch. While updating the doc I realized that perhaps we should have the new section for heap and put the descriptions of heap functions into it rather than having them as general functions. If we need this change it is for PG12. I will register only the new feature patch to the next Commit Fest. Please review them. Regards, -- Masahiko Sawada NIPPON TELEGRAPH AND TELEPHONE CORPORATION NTT Open Source Software Center
diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml index 8d81f88..0b92025 100644 --- a/doc/src/sgml/pageinspect.sgml +++ b/doc/src/sgml/pageinspect.sgml @@ -127,6 +127,35 @@ test=# SELECT page_checksum(get_raw_page('pg_class', 0), 0); <varlistentry> <term> + <function>fsm_page_contents(page bytea) returns text</function> + <indexterm> + <primary>fsm_page_contents</primary> + </indexterm> + </term> + + <listitem> + <para> + <function>fsm_page_contents</function> shows the internal node structure + of a FSM page. The output is a multiline string, with one line per + node in the binary tree within the page. Only those nodes that are not + zero are printed. The so-called "next" pointer, which points to the + next slot to be returned from the page, is also printed. + </para> + <para> + See <filename>src/backend/storage/freespace/README</filename> for more + information on the structure of an FSM page. + </para> + </listitem> + </varlistentry> + </variablelist> + </sect2> + + <sect2> + <title>Heap Functions</title> + + <variablelist> + <varlistentry> + <term> <function>heap_page_items(page bytea) returns setof record</function> <indexterm> <primary>heap_page_items</primary> @@ -203,29 +232,6 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class </para> </listitem> </varlistentry> - - <varlistentry> - <term> - <function>fsm_page_contents(page bytea) returns text</function> - <indexterm> - <primary>fsm_page_contents</primary> - </indexterm> - </term> - - <listitem> - <para> - <function>fsm_page_contents</function> shows the internal node structure - of a FSM page. The output is a multiline string, with one line per - node in the binary tree within the page. Only those nodes that are not - zero are printed. The so-called "next" pointer, which points to the - next slot to be returned from the page, is also printed. - </para> - <para> - See <filename>src/backend/storage/freespace/README</filename> for more - information on the structure of an FSM page. - </para> - </listitem> - </varlistentry> </variablelist> </sect2>
From 018077d786f874cb314b5f61b5ef85f42c62bbe5 Mon Sep 17 00:00:00 2001 From: Masahiko Sawada <sawada.mshk@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