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
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