v5-0001-Introduce-heap_tuple_infomask_flags-to-decode-t_i.patch

text/x-patch

Filename: v5-0001-Introduce-heap_tuple_infomask_flags-to-decode-t_i.patch
Type: text/x-patch
Part: 1
Message: Re: [HACKERS] [PATCH] pageinspect function to decode infomasks

Patch

Format: format-patch
Series: patch v5-0001
Subject: Introduce heap_tuple_infomask_flags to decode t_infomask and t_infomask2
File+
contrib/pageinspect/expected/page.out 136 0
contrib/pageinspect/heapfuncs.c 112 0
contrib/pageinspect/Makefile 1 1
contrib/pageinspect/pageinspect--1.7--1.8.sql 15 0
contrib/pageinspect/pageinspect.control 1 1
contrib/pageinspect/sql/page.sql 32 0
doc/src/sgml/pageinspect.sgml 39 0
From 45f3f833628a464e77f94b3c7bb4a74f15d3ecfc Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Tue, 10 Sep 2019 19:55:55 +0800
Subject: [PATCH v5] Introduce heap_tuple_infomask_flags to decode t_infomask
 and t_infomask2

---
 contrib/pageinspect/Makefile                  |   2 +-
 contrib/pageinspect/expected/page.out         | 136 ++++++++++++++++++
 contrib/pageinspect/heapfuncs.c               | 112 +++++++++++++++
 contrib/pageinspect/pageinspect--1.7--1.8.sql |  15 ++
 contrib/pageinspect/pageinspect.control       |   2 +-
 contrib/pageinspect/sql/page.sql              |  32 +++++
 doc/src/sgml/pageinspect.sgml                 |  39 +++++
 7 files changed, 336 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 e5a581f141..cfe01297fb 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 3fcd9fbe6d..4dea50a67c 100644
--- a/contrib/pageinspect/expected/page.out
+++ b/contrib/pageinspect/expected/page.out
@@ -82,6 +82,142 @@ 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. we show raw flags by
+-- default: HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID.
+VACUUM FREEZE test1;
+SELECT t_infomask, t_infomask2, unnest(flags)
+FROM heap_page_items(get_raw_page('test1', 0)),
+     LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(flags)
+ORDER BY 3;
+ t_infomask | t_infomask2 |       unnest        
+------------+-------------+---------------------
+       2816 |           2 | HEAP_XMAX_INVALID
+       2816 |           2 | HEAP_XMIN_COMMITTED
+       2816 |           2 | HEAP_XMIN_INVALID
+(3 rows)
+
+-- output the decoded flag HEAP_XMIN_FROZEN instead
+SELECT t_infomask, t_infomask2, unnest(flags)
+FROM heap_page_items(get_raw_page('test1', 0)),
+     LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2, true) m(flags)
+ORDER BY 3;
+ t_infomask | t_infomask2 |      unnest       
+------------+-------------+-------------------
+       2816 |           2 | HEAP_XMAX_INVALID
+       2816 |           2 | HEAP_XMIN_FROZEN
+(2 rows)
+
+-- test for HEAP_LOCKED_UPGRADED
+SELECT unnest(heap_tuple_infomask_flags(x'1080'::integer, 0, true)) ORDER BY 1;
+        unnest        
+----------------------
+ HEAP_LOCKED_UPGRADED
+(1 row)
+
+-- test for all flags of both t_infomask and t_infomask2
+SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::integer, x'FFFF'::integer, false)) ORDER BY 1;
+        unnest         
+-----------------------
+ HEAP_COMBOCID
+ HEAP_HASEXTERNAL
+ HEAP_HASNULL
+ HEAP_HASOID_OLD
+ HEAP_HASVARWIDTH
+ HEAP_HOT_UPDATED
+ HEAP_KEYS_UPDATED
+ HEAP_MOVED_IN
+ HEAP_MOVED_OFF
+ HEAP_ONLY_TUPLE
+ HEAP_UPDATED
+ HEAP_XMAX_COMMITTED
+ HEAP_XMAX_EXCL_LOCK
+ HEAP_XMAX_INVALID
+ HEAP_XMAX_IS_MULTI
+ HEAP_XMAX_KEYSHR_LOCK
+ HEAP_XMAX_LOCK_ONLY
+ HEAP_XMIN_COMMITTED
+ HEAP_XMIN_INVALID
+(19 rows)
+
+SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::integer, x'FFFF'::integer, true)) ORDER BY 1;
+       unnest        
+---------------------
+ HEAP_COMBOCID
+ HEAP_HASEXTERNAL
+ HEAP_HASNULL
+ HEAP_HASOID_OLD
+ HEAP_HASVARWIDTH
+ HEAP_HOT_UPDATED
+ HEAP_KEYS_UPDATED
+ HEAP_MOVED
+ HEAP_ONLY_TUPLE
+ HEAP_UPDATED
+ HEAP_XMAX_COMMITTED
+ HEAP_XMAX_INVALID
+ HEAP_XMAX_IS_MULTI
+ HEAP_XMAX_LOCK_ONLY
+ HEAP_XMAX_SHR_LOCK
+ HEAP_XMIN_FROZEN
+(16 rows)
+
+-- same result as specifying all flags
+SELECT unnest(heap_tuple_infomask_flags(-1, -1, false)) ORDER BY 1;
+        unnest         
+-----------------------
+ HEAP_COMBOCID
+ HEAP_HASEXTERNAL
+ HEAP_HASNULL
+ HEAP_HASOID_OLD
+ HEAP_HASVARWIDTH
+ HEAP_HOT_UPDATED
+ HEAP_KEYS_UPDATED
+ HEAP_MOVED_IN
+ HEAP_MOVED_OFF
+ HEAP_ONLY_TUPLE
+ HEAP_UPDATED
+ HEAP_XMAX_COMMITTED
+ HEAP_XMAX_EXCL_LOCK
+ HEAP_XMAX_INVALID
+ HEAP_XMAX_IS_MULTI
+ HEAP_XMAX_KEYSHR_LOCK
+ HEAP_XMAX_LOCK_ONLY
+ HEAP_XMIN_COMMITTED
+ HEAP_XMIN_INVALID
+(19 rows)
+
+SELECT unnest(heap_tuple_infomask_flags(-1, -1, true)) ORDER BY 1;
+       unnest        
+---------------------
+ HEAP_COMBOCID
+ HEAP_HASEXTERNAL
+ HEAP_HASNULL
+ HEAP_HASOID_OLD
+ HEAP_HASVARWIDTH
+ HEAP_HOT_UPDATED
+ HEAP_KEYS_UPDATED
+ HEAP_MOVED
+ HEAP_ONLY_TUPLE
+ HEAP_UPDATED
+ HEAP_XMAX_COMMITTED
+ HEAP_XMAX_INVALID
+ HEAP_XMAX_IS_MULTI
+ HEAP_XMAX_LOCK_ONLY
+ HEAP_XMAX_SHR_LOCK
+ HEAP_XMIN_FROZEN
+(16 rows)
+
+-- output no flags
+SELECT unnest(heap_tuple_infomask_flags(0, 0, false)) ORDER BY 1;
+ unnest 
+--------
+(0 rows)
+
+SELECT unnest(heap_tuple_infomask_flags(0, 0, true)) ORDER BY 1;
+ unnest 
+--------
+(0 rows)
+
 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 64a6e351d5..3764bf375c 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,114 @@ tuple_data_split(PG_FUNCTION_ARGS)
 
 	PG_RETURN_ARRAYTYPE_P(res);
 }
+
+/*
+ * heap_tuple_infomask_flags
+ *
+ * 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_tuple_infomask_flags);
+
+Datum
+heap_tuple_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;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser to use raw page functions")));
+
+	bitcnt = pg_popcount((const char *) &t_infomask, sizeof(uint16)) +
+		pg_popcount((const char *) &t_infomask2, sizeof(uint16));
+
+	/* If no flags, return an empty array */
+	if (bitcnt <= 0)
+		PG_RETURN_POINTER(construct_empty_array(TEXTOID));
+
+	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');
+
+	pfree(d);
+
+	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 0000000000..7e85677d6c
--- /dev/null
+++ b/contrib/pageinspect/pageinspect--1.7--1.8.sql
@@ -0,0 +1,15 @@
+/* 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
+
+--
+-- heap_tuple_infomask_flags()
+--
+CREATE FUNCTION heap_tuple_infomask_flags(
+       t_infomask integer,
+       t_infomask2 integer,
+       decode_combined boolean DEFAULT false)
+RETURNS text[]
+AS 'MODULE_PATHNAME', 'heap_tuple_infomask_flags'
+LANGUAGE C STRICT PARALLEL SAFE;
diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control
index dcfc61f22d..f8cdf526c6 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 8ac9991837..8d3b69aa2b 100644
--- a/contrib/pageinspect/sql/page.sql
+++ b/contrib/pageinspect/sql/page.sql
@@ -31,6 +31,38 @@ 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. we show raw flags by
+-- default: HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID.
+VACUUM FREEZE test1;
+
+SELECT t_infomask, t_infomask2, unnest(flags)
+FROM heap_page_items(get_raw_page('test1', 0)),
+     LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(flags)
+ORDER BY 3;
+
+-- output the decoded flag HEAP_XMIN_FROZEN instead
+SELECT t_infomask, t_infomask2, unnest(flags)
+FROM heap_page_items(get_raw_page('test1', 0)),
+     LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2, true) m(flags)
+ORDER BY 3;
+
+-- test for HEAP_LOCKED_UPGRADED
+SELECT unnest(heap_tuple_infomask_flags(x'1080'::integer, 0, true)) ORDER BY 1;
+
+-- test for all flags of both t_infomask and t_infomask2
+SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::integer, x'FFFF'::integer, false)) ORDER BY 1;
+SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::integer, x'FFFF'::integer, true)) ORDER BY 1;
+
+-- same result as specifying all flags
+SELECT unnest(heap_tuple_infomask_flags(-1, -1, false)) ORDER BY 1;
+SELECT unnest(heap_tuple_infomask_flags(-1, -1, true)) ORDER BY 1;
+
+-- output no flags
+SELECT unnest(heap_tuple_infomask_flags(0, 0, false)) ORDER BY 1;
+SELECT unnest(heap_tuple_infomask_flags(0, 0, true)) ORDER BY 1;
+
+
 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 7a767b25ea..367b6327b7 100644
--- a/doc/src/sgml/pageinspect.sgml
+++ b/doc/src/sgml/pageinspect.sgml
@@ -184,6 +184,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_tuple_infomask_flags</function> function can be used to unpack the
+      recognized bits of the infomasks of heap tuples.
+     </para>
     </listitem>
    </varlistentry>
 
@@ -236,6 +240,41 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>heap_tuple_infomask_flags(t_infomask integer, t_infomask2 integer, decode_combined bool) returns text[]</function>
+     <indexterm>
+      <primary>heap_tuple_infomask_flags</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      <function>heap_tuple_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.  For example:
+<screen>
+test=# SELECT heap_tuple_infomask_flags(t_infomask, t_infomask2, true) FROM heap_page_items(get_raw_page('pg_class', 0));
+</screen>
+      This function should be called with the same arguments as the return
+      attributes of <function>heap_page_items</function>.
+     </para>
+     <para>
+      If <parameter>decode_combined</parameter> is <literal>true</literal>,
+      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.22.0