[PATCH] pageinspect function to decode infomasks
Hi
Whenever I'm debugging some kind of corruption incident, possible
visibility bug, etc, I always land up staring at integer infomasks or using
a SQL helper function to decode them.
That's silly, so here's a patch to teach pageinspect how to decode
infomasks to a human readable array of flag names.
Example:
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)
To decode individual mask integers you can just call it directly. It's
strict, so pass 0 for the other mask if you don't have both, e.g.
SELECT heap_infomask_flags(2816, 0);
The patch backports easily to older pageinspect versions for when you're
debugging something old.
BTW, I used text[] not enums. That costs a fair bit of memory, but it
doesn't seem worth worrying too much about in this context.
For convenience it also tests and reports HEAP_LOCKED_UPGRADED and
HEAP_XMAX_IS_LOCKED_ONLY as pseudo-flags.
I decided not to filter
out HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID
when HEAP_XMIN_FROZEN is set; that doesn't make sense when we
examine HEAP_XMAX_IS_LOCKED_ONLY or HEAP_LOCKED_UPGRADED, and filtering
them out could be just as confusing as leaving them in.
The infomask2 natts mask is ignored. You can bitwise-and it out in SQL
pretty easily if needed. I could output it here as a constructed text
datum, but it seems mostly pointless.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
v1-0001-Introduce-heap_infomask_flags-to-decode-infomask-.patchtext/x-patch; charset=US-ASCII; name=v1-0001-Introduce-heap_infomask_flags-to-decode-infomask-.patchDownload
From 488a1f69b8082258d508ba681a4f4a5f6fce2267 Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig@2ndquadrant.com>
Date: Thu, 20 Jul 2017 11:20:21 +0800
Subject: [PATCH v1] Introduce heap_infomask_flags to decode infomask and
infomask2
---
contrib/pageinspect/Makefile | 3 +-
contrib/pageinspect/expected/page.out | 25 ++++++
contrib/pageinspect/heapfuncs.c | 120 ++++++++++++++++++++++++++
contrib/pageinspect/pageinspect--1.6--1.7.sql | 9 ++
contrib/pageinspect/pageinspect.control | 2 +-
contrib/pageinspect/sql/page.sql | 14 +++
doc/src/sgml/pageinspect.sgml | 32 +++++++
7 files changed, 203 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..054c69d 100644
--- a/contrib/pageinspect/expected/page.out
+++ b/contrib/pageinspect/expected/page.out
@@ -82,6 +82,31 @@ 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}
+(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..17bea2a 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);
+ maxarray += HEAP_LOCKED_UPGRADED(t_infomask);
+ }
+
+ d = (Datum *) palloc(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, maxarray - 1, 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..b7a1f0b 100644
--- a/contrib/pageinspect/sql/page.sql
+++ b/contrib/pageinspect/sql/page.sql
@@ -31,6 +31,20 @@ 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);
+
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..24925a0 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</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, show_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 show_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
On 20 July 2017 at 11:33, Craig Ringer <craig@2ndquadrant.com> wrote:
Hi
Whenever I'm debugging some kind of corruption incident, possible
visibility bug, etc, I always land up staring at integer infomasks or using
a SQL helper function to decode them.That's silly, so here's a patch to teach pageinspect how to decode
infomasks to a human readable array of flag names.Example:
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)To decode individual mask integers you can just call it directly. It's
strict, so pass 0 for the other mask if you don't have both, e.g.SELECT heap_infomask_flags(2816, 0);
The patch backports easily to older pageinspect versions for when you're
debugging something old.BTW, I used text[] not enums. That costs a fair bit of memory, but it
doesn't seem worth worrying too much about in this context.For convenience it also tests and reports HEAP_LOCKED_UPGRADED and
HEAP_XMAX_IS_LOCKED_ONLY as pseudo-flags.I decided not to filter out HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID
when HEAP_XMIN_FROZEN is set
Er, decided not to filter out HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID.
Obviously wouldn't filter out HEAP_XMAX_INVALID, that was a copy-paste'o.
I wonder if it's worth dropping the HEAP_ prefix. Meh, anyway, usable as-is.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Wed, Jul 19, 2017 at 8:33 PM, Craig Ringer <craig@2ndquadrant.com> wrote:
That's silly, so here's a patch to teach pageinspect how to decode infomasks
to a human readable array of flag names.Example:
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)
Seems like a good idea to me.
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Jul 20, 2017 at 5:44 AM, Peter Geoghegan <pg@bowt.ie> wrote:
On Wed, Jul 19, 2017 at 8:33 PM, Craig Ringer <craig@2ndquadrant.com> wrote:
That's silly, so here's a patch to teach pageinspect how to decode infomasks
to a human readable array of flag names.Example:
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)Seems like a good idea to me.
+1, it'll be really helpful.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Jul 20, 2017 at 3:13 PM, Julien Rouhaud <rjuju123@gmail.com> wrote:
On Thu, Jul 20, 2017 at 5:44 AM, Peter Geoghegan <pg@bowt.ie> wrote:
On Wed, Jul 19, 2017 at 8:33 PM, Craig Ringer <craig@2ndquadrant.com> wrote:
That's silly, so here's a patch to teach pageinspect how to decode infomasks
to a human readable array of flag names.Example:
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)Seems like a good idea to me.
+1, it'll be really helpful.
+1.
When I investigated data corruption incident I also wrote a plpgsql
function for the same purpose, and it was very useful. I think we can
have the similar thing for lp_flags as well.
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
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
#4 0x000000000082f4cd in ExecInterpExpr (state=0x2ad3aa0,
econtext=0x2ad3750, isnull=0x7ffc0b0bbf67 "") at execExprInterp.c:672
#5 0x000000000088b832 in ExecEvalExprSwitchContext (state=0x2ad3aa0,
econtext=0x2ad3750, isNull=0x7ffc0b0bbf67 "")
at ../../../src/include/executor/executor.h:290
#6 0x000000000088b8e3 in ExecProject (projInfo=0x2ad3a98) at
../../../src/include/executor/executor.h:324
#7 0x000000000088bb89 in ExecResult (node=0x2ad36b8) at nodeResult.c:132
#8 0x00000000008494fe in ExecProcNode (node=0x2ad36b8) at execProcnode.c:416
#9 0x000000000084125d in ExecutePlan (estate=0x2ad34a0,
planstate=0x2ad36b8, use_parallel_mode=0 '\000', operation=CMD_SELECT,
sendTuples=1 '\001',
numberTuples=0, direction=ForwardScanDirection, dest=0x2ac0ae0,
execute_once=1 '\001') at execMain.c:1693
#10 0x000000000083d54b in standard_ExecutorRun (queryDesc=0x2a42880,
direction=ForwardScanDirection, count=0, execute_once=1 '\001') at
execMain.c:362
#11 0x000000000083d253 in ExecutorRun (queryDesc=0x2a42880,
direction=ForwardScanDirection, count=0, execute_once=1 '\001') at
execMain.c:305
#12 0x0000000000b3dd8f in PortalRunSelect (portal=0x2ad1490, forward=1
'\001', count=0, dest=0x2ac0ae0) at pquery.c:932
#13 0x0000000000b3d7e7 in PortalRun (portal=0x2ad1490,
count=9223372036854775807, isTopLevel=1 '\001', run_once=1 '\001',
dest=0x2ac0ae0, altdest=0x2ac0ae0,
completionTag=0x7ffc0b0bc2c0 "") at pquery.c:773
#14 0x0000000000b31fe4 in exec_simple_query (query_string=0x2a9d9a0
"SELECT heap_infomask_flags(11008, 1111, true);") at postgres.c:1099
#15 0x0000000000b3a727 in PostgresMain (argc=1, argv=0x2a49eb0,
dbname=0x2a1d480 "postgres", username=0x2a49d18 "ashu") at
postgres.c:4090
#16 0x0000000000a2cb3f in BackendRun (port=0x2a3e700) at postmaster.c:4357
#17 0x0000000000a2bc63 in BackendStartup (port=0x2a3e700) at postmaster.c:4029
#18 0x0000000000a248ab in ServerLoop () at postmaster.c:1753
#19 0x0000000000a236a9 in PostmasterMain (argc=3, argv=0x2a1b2b0) at
postmaster.c:1361
#20 0x00000000008d8054 in main (argc=3, argv=0x2a1b2b0) at main.c:228
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>
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.
postgres=# SELECT heap_infomask_flags(2816, 0);
heap_infomask_flags
-----------------------------------------------------------
{HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID}
(1 row)
postgres=# SELECT heap_infomask_flags(2816, 1);
heap_infomask_flags
----------------------------------------------------------------------------
{HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN}
(1 row)
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'.
--
With Regards,
Ashutosh Sharma
EnterpriseDB:http://www.enterprisedb.com
On Thu, Jul 20, 2017 at 11:56 AM, Masahiko Sawada <sawada.mshk@gmail.com> wrote:
On Thu, Jul 20, 2017 at 3:13 PM, Julien Rouhaud <rjuju123@gmail.com> wrote:
On Thu, Jul 20, 2017 at 5:44 AM, Peter Geoghegan <pg@bowt.ie> wrote:
On Wed, Jul 19, 2017 at 8:33 PM, Craig Ringer <craig@2ndquadrant.com> wrote:
That's silly, so here's a patch to teach pageinspect how to decode infomasks
to a human readable array of flag names.Example:
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)Seems like a good idea to me.
+1, it'll be really helpful.
+1.
When I investigated data corruption incident I also wrote a plpgsql
function for the same purpose, and it was very useful. I think we can
have the similar thing for lp_flags as well.Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 20 Jul. 2017 19:09, "Ashutosh Sharma" <ashu.coek88@gmail.com> wrote:
I had a quick look into this patch and also tested it and following
are my observations.
Thanks very much.
I'll expand the tests to cover various normal and nonsensical masks and
combinations and fix the identified issues.
This was a quick morning's work in amongst other things so not surprised I
missed a few details. The check is appreciated.
On 20 July 2017 at 19:09, Ashutosh Sharma <ashu.coek88@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.
!> \qFollowing 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
Attachments:
v2-0001-Introduce-heap_infomask_flags-to-decode-infomask-.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Introduce-heap_infomask_flags-to-decode-infomask-.patchDownload
From a25c1e50e3b3ff493a018ab594a71dc3d64832ee Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig@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
Dear Craig Ringer
Frist, thank you for implementing the necessary function.
but, i have some question.
question 1) vacuum freeze hint bits
If run a vacuum freeze, bits in the infomask will be 0x0300.
in this case, if output the value of informsk in the run to you modified,
HEAP_XMIN_COMMITTED(0x0100), HEAP_XMIN_INVALID(0x0200), HEAP_XMIN_FROZEN(0x0300)
all outputs to hint bits.
is it normal to output values?
if look at htup_details.h code,
#define HeapTupleHeaderXminInvalid(tup) \
( \
((tup)->t_infomask & (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)) == \
HEAP_XMIN_INVALID \
)
#define HeapTupleHeaderSetXminCommitted(tup) \
( \
AssertMacro(!HeapTupleHeaderXminInvalid(tup)), \
((tup)->t_infomask |= HEAP_XMIN_COMMITTED) \
)
HEAP_XMIN_INVALID and HEAP_XMIN_COMMITTED can not be write simultaneously.
So I think the value of 0x0300 is to HEAP_XMIN_COMMITTED, HEAP_XMIN_FROZEN
Only output needs to be values.
question 2) xmax lock hint bits
similar to the vacuum freezeze question..
Assume that the infomask has a bit of 0x0050
In this case, if run on the code that you modified,
HEAP_XMAX_KEYSHR_LOCK(0x0010), HEAP_XMAX_EXCL_LOCK(0x0040), HEAP_XMAX_IS_LOCKED_ONLY
three hint bits are the output.
if look at htup_details.h code,
#define HEAP_XMAX_IS_SHR_LOCKED(infomask) \
(((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_SHR_LOCK)
#define HEAP_XMAX_IS_EXCL_LOCKED(infomask) \
(((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_EXCL_LOCK)
#define HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) \
(((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_KEYSHR_LOCK)
It is divided into to hint bits.
so I think this part needs to fix.
If my opinion may be wrong. So plz check one more time.
Regards.
Moon
From: pgsql-hackers-owner@postgresql.org [mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Craig Ringer
Sent: Thursday, July 20, 2017 8:53 PM
To: Ashutosh Sharma
Cc: PostgreSQL Hackers; Julien Rouhaud; Pavan Deolasee; Álvaro Herrera; Peter Eisentraut; Masahiko Sawada; abhijit Menon-Sen; Peter Geoghegan
Subject: Re: [HACKERS] [PATCH] pageinspect function to decode infomasks
On 20 Jul. 2017 19:09, "Ashutosh Sharma" <ashu.coek88@gmail.com <mailto:ashu.coek88@gmail.com> > wrote:
I had a quick look into this patch and also tested it and following
are my observations.
Thanks very much.
I'll expand the tests to cover various normal and nonsensical masks and combinations and fix the identified issues.
This was a quick morning's work in amongst other things so not surprised I missed a few details. The check is appreciated.
On 15 August 2017 at 09:11, Moon Insung <Moon_Insung_i3@lab.ntt.co.jp>
wrote:
Dear Craig Ringer
Frist, thank you for implementing the necessary function.
but, i have some question.
question 1) vacuum freeze hint bits
If run a vacuum freeze, bits in the infomask will be 0x0300.
in this case, if output the value of informsk in the run to you modified,
HEAP_XMIN_COMMITTED(0x0100), HEAP_XMIN_INVALID(0x0200),
HEAP_XMIN_FROZEN(0x0300)all outputs to hint bits.
is it normal to output values?
if look at htup_details.h code,
#define HeapTupleHeaderXminInvalid(tup) \
( \
((tup)->t_infomask & (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID))
== \HEAP_XMIN_INVALID \
)
#define HeapTupleHeaderSetXminCommitted(tup) \
( \
AssertMacro(!HeapTupleHeaderXminInvalid(tup)), \
((tup)->t_infomask |= HEAP_XMIN_COMMITTED) \
)
HEAP_XMIN_INVALID and HEAP_XMIN_COMMITTED can not be write simultaneously.
The bits are set, those macros just test to exclude the special meaning of
both bits being set at once to mean "frozen".
I was reluctant to filter out HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID
when we detect that it's frozen, because that could well be misleading when
debugging.
If you think that is useful, then I suggest you add an option so that when
it's outputting the interpreted mask not the raw mask, it suppresses output
of HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID if HEAP_XMIN_FROZEN.
question 2) xmax lock hint bits
similar to the vacuum freezeze question..
Assume that the infomask has a bit of 0x0050
In this case, if run on the code that you modified,
HEAP_XMAX_KEYSHR_LOCK(0x0010), HEAP_XMAX_EXCL_LOCK(0x0040),
HEAP_XMAX_IS_LOCKED_ONLYthree hint bits are the output.
if look at htup_details.h code,
#define HEAP_XMAX_IS_SHR_LOCKED(infomask) \
(((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_SHR_LOCK)
#define HEAP_XMAX_IS_EXCL_LOCKED(infomask) \
(((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_EXCL_LOCK)
#define HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) \
(((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_KEYSHR_LOCK)
It is divided into to hint bits.
so I think this part needs to fix.
It's the same issue as above, with the same answer IMO.
If we're showing the raw mask bits we should show the raw mask bits only.
But if we're showing combined bits and masks too, I guess we should filter
out the raw bits when matched by some mask.
I'm not entirely convinced by that, since I think hiding information could
create more confusion than it fixes. I welcome others' views here.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Mon, Aug 14, 2017 at 9:59 PM, Craig Ringer <craig@2ndquadrant.com> wrote:
The bits are set, those macros just test to exclude the special meaning of
both bits being set at once to mean "frozen".I was reluctant to filter out HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID
when we detect that it's frozen, because that could well be misleading when
debugging.
I don't think so -- the "committed" and "invalid" meanings are
effectively canceled when the "frozen" mask is present.
I mean, "committed" and "invalid" contradict each other...
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 08/15/2017 03:24 PM, Robert Haas wrote:
On Mon, Aug 14, 2017 at 9:59 PM, Craig Ringer <craig@2ndquadrant.com> wrote:
The bits are set, those macros just test to exclude the special meaning of
both bits being set at once to mean "frozen".I was reluctant to filter out HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID
when we detect that it's frozen, because that could well be misleading when
debugging.I don't think so -- the "committed" and "invalid" meanings are
effectively canceled when the "frozen" mask is present.I mean, "committed" and "invalid" contradict each other...
FWIW I agree with Craig - the functions should output the masks raw,
without any filtering. The reason is that when you're investigating data
corruption or unexpected behavior, all this is very useful when
reasoning about what might (not) have happened.
Or at least make the filtering optional.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Aug 15, 2017 at 10:59 PM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
On 08/15/2017 03:24 PM, Robert Haas wrote:
On Mon, Aug 14, 2017 at 9:59 PM, Craig Ringer <craig@2ndquadrant.com>
wrote:The bits are set, those macros just test to exclude the special meaning
of
both bits being set at once to mean "frozen".I was reluctant to filter out HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID
when we detect that it's frozen, because that could well be misleading
when
debugging.I don't think so -- the "committed" and "invalid" meanings are
effectively canceled when the "frozen" mask is present.I mean, "committed" and "invalid" contradict each other...
FWIW I agree with Craig - the functions should output the masks raw, without
any filtering. The reason is that when you're investigating data corruption
or unexpected behavior, all this is very useful when reasoning about what
might (not) have happened.Or at least make the filtering optional.
I'd vote for having both and making one optional (perhaps filtering?).
Both are useful to me for the debugging and study purpose.
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Aug 15, 2017 at 9:59 AM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
I don't think so -- the "committed" and "invalid" meanings are
effectively canceled when the "frozen" mask is present.I mean, "committed" and "invalid" contradict each other...
FWIW I agree with Craig - the functions should output the masks raw, without
any filtering. The reason is that when you're investigating data corruption
or unexpected behavior, all this is very useful when reasoning about what
might (not) have happened.Or at least make the filtering optional.
I don't think "filtering" is the right way to think about it. It's
just labeling each combination of bits with the meaning appropriate to
that combination of bits.
I mean, if you were displaying the contents of a CLOG entry, would you
want the value 3 to be displayed as COMMITTED ABORTED SUBCOMMITTED
because TRANSACTION_STATUS_COMMITTED|TRANSACTION_STATUS_ABORTED ==
TRANSACTION_STATUS_SUB_COMMITTED?
I realize that you may be used to thinking of the HEAP_XMIN_COMMITTED
and HEAP_XMAX_COMMITTED bits as two separate bits, but that's not
really true any more. They're a 2-bit field that can have one of four
values: committed, aborted, frozen, or none of the above.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 08/15/2017 07:54 PM, Robert Haas wrote:
On Tue, Aug 15, 2017 at 9:59 AM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:I don't think so -- the "committed" and "invalid" meanings are
effectively canceled when the "frozen" mask is present.I mean, "committed" and "invalid" contradict each other...
FWIW I agree with Craig - the functions should output the masks raw, without
any filtering. The reason is that when you're investigating data corruption
or unexpected behavior, all this is very useful when reasoning about what
might (not) have happened.Or at least make the filtering optional.
I don't think "filtering" is the right way to think about it. It's
just labeling each combination of bits with the meaning appropriate to
that combination of bits.I mean, if you were displaying the contents of a CLOG entry, would you
want the value 3 to be displayed as COMMITTED ABORTED SUBCOMMITTED
because TRANSACTION_STATUS_COMMITTED|TRANSACTION_STATUS_ABORTED ==
TRANSACTION_STATUS_SUB_COMMITTED?I realize that you may be used to thinking of the HEAP_XMIN_COMMITTED
and HEAP_XMAX_COMMITTED bits as two separate bits, but that's not
really true any more. They're a 2-bit field that can have one of four
values: committed, aborted, frozen, or none of the above.
All I'm saying is that having the complete information (knowing which
bits are actually set in the bitmask) is valuable when reasoning about
how you might have gotten to the current state. Which I think is what
Craig is after.
What I think we should not do is interpret the bitmasks (omitting some
of the information) assuming all the bits were set correctly.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Aug 15, 2017 at 3:42 PM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
What I think we should not do is interpret the bitmasks (omitting some of
the information) assuming all the bits were set correctly.
I'm still confused. HEAP_XMIN_COMMITTED|HEAP_XMIN_ABORTED ==
HEAP_XMIN_FROZEN. Nobody is proposing to omit anything; to the
contrary, what's being proposed is not to display the same thing twice
(and in a misleading fashion, to boot).
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 08/15/2017 09:55 PM, Robert Haas wrote:
On Tue, Aug 15, 2017 at 3:42 PM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:What I think we should not do is interpret the bitmasks (omitting some of
the information) assuming all the bits were set correctly.I'm still confused. HEAP_XMIN_COMMITTED|HEAP_XMIN_ABORTED ==
HEAP_XMIN_FROZEN. Nobody is proposing to omit anything; to the
contrary, what's being proposed is not to display the same thing
twice (and in a misleading fashion, to boot).
I understand your point. Assume you're looking at this bit of code:
if (HeapTupleHeaderXminCommitted(enumval_tup->t_data))
return;
which is essentially
if (enumval_tup->t_data & HEAP_XMIN_COMMITTED)
return;
If the function only gives you HEAP_XMIN_FROZEN, how likely is it you
miss this actually evaluates as true?
You might say that people investigating issues in this area of code
should be aware of how HEAP_XMIN_FROZEN is defined, and perhaps you're
right ...
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I checked for code related to infomask.
(add flag state -- HEAP_XMIN_COMMITTED, HEAP_XMIN_INVALID, HEAP_XMIN_FROZEN)
first i'm still beginner level about postgresql, so my opinion may be wrong.
if the "HEAP_XMIN_COMMITTED" flag is added, check the function of "HeapTupleHeaderXminInvalid"
if the "HEAP_XMIN_INVALID" flag is added, check the function of "HeapTupleHeaderXminCommitted"
if the "HEAP_XMIN_FROZEN" flag is added, use the "HeapTupleHeaderSetXminFrozen" function or
use the code as
--------------------------------------
xid = HeapTupleHeaderGetXmin(tuple);
if (TransactionIdIsNormal(xid))
{
if (TransactionIdPrecedes(xid, cutoff_xid))
{
frz->t_infomask |= HEAP_XMIN_FROZEN;
changed = true;
}
else
totally_frozen = false;
}
--------------------------------------
to add the flag.
so as a result, HEAP_XMIN_INVALID and HEAP_XMIN_COMMITTED is cannot coexist.
unfortunately, i don't know if HEAP_XMIN_COMMITTED and HEAP_XMIN_FROZEN flags can coexist.
so i think it's also a good idea to output the raw masks, without any filtering.
however, i think the information that is presented to the user should inform us which flags was entered.
Regards.
Moon
-----Original Message-----
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Tomas Vondra
Sent: Wednesday, August 16, 2017 5:36 AM
To: Robert Haas
Cc: pgsql-hackers@postgresql.org
Subject: Re: [HACKERS] [PATCH] pageinspect function to decode infomasksOn 08/15/2017 09:55 PM, Robert Haas wrote:
On Tue, Aug 15, 2017 at 3:42 PM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:What I think we should not do is interpret the bitmasks (omitting
some of the information) assuming all the bits were set correctly.I'm still confused. HEAP_XMIN_COMMITTED|HEAP_XMIN_ABORTED ==
HEAP_XMIN_FROZEN. Nobody is proposing to omit anything; to the
contrary, what's being proposed is not to display the same thing twice
(and in a misleading fashion, to boot).I understand your point. Assume you're looking at this bit of code:
if (HeapTupleHeaderXminCommitted(enumval_tup->t_data))
return;which is essentially
if (enumval_tup->t_data & HEAP_XMIN_COMMITTED)
return;If the function only gives you HEAP_XMIN_FROZEN, how likely is it you miss
this actually evaluates as true?You might say that people investigating issues in this area of code should
be aware of how HEAP_XMIN_FROZEN is defined, and perhaps you're right ...regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make
changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 16 August 2017 at 03:42, Tomas Vondra <tomas.vondra@2ndquadrant.com>
wrote:
On 08/15/2017 07:54 PM, Robert Haas wrote:
On Tue, Aug 15, 2017 at 9:59 AM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:I don't think so -- the "committed" and "invalid" meanings are
effectively canceled when the "frozen" mask is present.
I mean, "committed" and "invalid" contradict each other...
FWIW I agree with Craig - the functions should output the masks raw,
without
any filtering. The reason is that when you're investigating data
corruption
or unexpected behavior, all this is very useful when reasoning about what
might (not) have happened.Or at least make the filtering optional.
I don't think "filtering" is the right way to think about it. It's
just labeling each combination of bits with the meaning appropriate to
that combination of bits.I mean, if you were displaying the contents of a CLOG entry, would you
want the value 3 to be displayed as COMMITTED ABORTED SUBCOMMITTED
because TRANSACTION_STATUS_COMMITTED|TRANSACTION_STATUS_ABORTED ==
TRANSACTION_STATUS_SUB_COMMITTED?I realize that you may be used to thinking of the HEAP_XMIN_COMMITTED
and HEAP_XMAX_COMMITTED bits as two separate bits, but that's not
really true any more. They're a 2-bit field that can have one of four
values: committed, aborted, frozen, or none of the above.All I'm saying is that having the complete information (knowing which bits
are actually set in the bitmask) is valuable when reasoning about how you
might have gotten to the current state. Which I think is what Craig is
after.What I think we should not do is interpret the bitmasks (omitting some of
the information) assuming all the bits were set correctly.
I agree, and the patch already does half of this: it can output just the
raw bit flags, or it can interpret them to show HEAP_XMIN_FROZEN etc.
So the required change, which seems to have broad agreement, is to have the
"interpret the bits" mode show only HEAP_XMIN_FROZEN when it sees
HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID, etc. We can retain raw-flags output
as-is for when seriously bogus state is suspected.
Any takers?
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Tue, Aug 15, 2017 at 4:36 PM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:
You might say that people investigating issues in this area of code should
be aware of how HEAP_XMIN_FROZEN is defined, and perhaps you're right ...
Yes, I think that's what I would say. I mean, if you happen to NOT
know that committed|invalid == frozen, but you DO know what committed
means and what invalid means, then you're going to be *really*
confused when you see committed and invalid set on the same tuple.
Showing you frozen has got to be clearer.
Now, I agree with you that a test like (enumval_tup->t_data &
HEAP_XMIN_COMMITTED) could be confusing to someone who doesn't realize
that HEAP_XMIN_FROZEN & HEAP_XMIN_COMMITTED == HEAP_XMIN_COMMITTED,
but I think that's just one of those things that unfortunately is
going to require adequate knowledge for people investigating issues.
If there's an action item there, it might be to try to come up with a
way to make the source code clearer.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 16 August 2017 at 23:14, Robert Haas <robertmhaas@gmail.com> wrote:
On Tue, Aug 15, 2017 at 4:36 PM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:You might say that people investigating issues in this area of code
should
be aware of how HEAP_XMIN_FROZEN is defined, and perhaps you're right ...
Yes, I think that's what I would say. I mean, if you happen to NOT
know that committed|invalid == frozen, but you DO know what committed
means and what invalid means, then you're going to be *really*
confused when you see committed and invalid set on the same tuple.
Showing you frozen has got to be clearer.Now, I agree with you that a test like (enumval_tup->t_data &
HEAP_XMIN_COMMITTED) could be confusing to someone who doesn't realize
that HEAP_XMIN_FROZEN & HEAP_XMIN_COMMITTED == HEAP_XMIN_COMMITTED,
but I think that's just one of those things that unfortunately is
going to require adequate knowledge for people investigating issues.
If there's an action item there, it might be to try to come up with a
way to make the source code clearer.
For other multi-purpose flags we have macros, and I think it'd make sense
to use them here too.
Eschew direct use of HEAP_XMIN_COMMITTED, HEAP_XMIN_INVALID and
HEAP_XMIN_FROZEN in tests. Instead, consistently use HeapXminIsFrozen(),
HeapXminIsCommitted(), and HeapXminIsInvalid() or something like that.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Hi Craig,
On Thu, Aug 17, 2017 at 5:50 AM, Craig Ringer <craig@2ndquadrant.com> wrote:
On 16 August 2017 at 23:14, Robert Haas <robertmhaas@gmail.com> wrote:
On Tue, Aug 15, 2017 at 4:36 PM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:You might say that people investigating issues in this area of code
should
be aware of how HEAP_XMIN_FROZEN is defined, and perhaps you're right
...Yes, I think that's what I would say. I mean, if you happen to NOT
know that committed|invalid == frozen, but you DO know what committed
means and what invalid means, then you're going to be *really*
confused when you see committed and invalid set on the same tuple.
Showing you frozen has got to be clearer.Now, I agree with you that a test like (enumval_tup->t_data &
HEAP_XMIN_COMMITTED) could be confusing to someone who doesn't realize
that HEAP_XMIN_FROZEN & HEAP_XMIN_COMMITTED == HEAP_XMIN_COMMITTED,
but I think that's just one of those things that unfortunately is
going to require adequate knowledge for people investigating issues.
If there's an action item there, it might be to try to come up with a
way to make the source code clearer.For other multi-purpose flags we have macros, and I think it'd make sense to
use them here too.Eschew direct use of HEAP_XMIN_COMMITTED, HEAP_XMIN_INVALID and
HEAP_XMIN_FROZEN in tests. Instead, consistently use HeapXminIsFrozen(),
HeapXminIsCommitted(), and HeapXminIsInvalid() or something like that.--
Are you planning to work on the review comments from Robert, Moon
Insung and supply the new patch. I just had a quick glance into this
mail thread (after a long time) and could understand Robert's concern
till some extent. I think, he is trying to say that if a tuple is
frozen (committed|invalid) then it shouldn't be shown as COMMITTED and
INVALID together in fact it should just be displayed as FROZEN tuple.
--
With Regards,
Ashutosh Sharma
EnterpriseDB:http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 14 September 2017 at 19:57, Ashutosh Sharma <ashu.coek88@gmail.com>
wrote:
Are you planning to work on the review comments from Robert, Moon
Insung and supply the new patch. I just had a quick glance into this
mail thread (after a long time) and could understand Robert's concern
till some extent. I think, he is trying to say that if a tuple is
frozen (committed|invalid) then it shouldn't be shown as COMMITTED and
INVALID together in fact it should just be displayed as FROZEN tuple.
Yes, I'd like to, and should have time for it in this CF.
My plan is to emit raw flags by default, so FROZEN would't be shown at all,
only COMMITTED|INVALID. If the bool to decode combined flags is set, then
it'll show things like FROZEN, and hide COMMITTED|INVALID. Similar for
other combos.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Tue, Aug 15, 2017 at 10:54 AM, Robert Haas <robertmhaas@gmail.com> wrote:
Or at least make the filtering optional.
I don't think "filtering" is the right way to think about it. It's
just labeling each combination of bits with the meaning appropriate to
that combination of bits.
I do. -1 to not just showing what's on the page -- if the
HEAP_XMIN_COMMITTED and HEAP_XMIN_ABORTED bits are set, then I think
we should show them. Yeah, I accept that there is a real danger of
confusing people with that. Unfortunately, I think that displaying
HEAP_XMIN_FROZEN will cause even more confusion. I don't think that
HEAP_XMIN_FROZEN is an abstraction at all. It's a notational
convenience.
I don't think it's our place to "interpret" the bits. Are we *also*
going to show HEAP_XMIN_FROZEN when xmin is physically set to
FrozenTransactionId? Where does it end?
I think that we should prominently document that HEAP_XMIN_COMMITTED
|HEAP_XMIN_ABORTED == HEAP_XMIN_FROZEN, rather than trying to hide
complexity that we have no business hiding in a tool like pageinspect.
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Oct 12, 2017 at 7:35 PM, Peter Geoghegan <pg@bowt.ie> wrote:
On Tue, Aug 15, 2017 at 10:54 AM, Robert Haas <robertmhaas@gmail.com> wrote:
Or at least make the filtering optional.
I don't think "filtering" is the right way to think about it. It's
just labeling each combination of bits with the meaning appropriate to
that combination of bits.I do. -1 to not just showing what's on the page -- if the
HEAP_XMIN_COMMITTED and HEAP_XMIN_ABORTED bits are set, then I think
we should show them. Yeah, I accept that there is a real danger of
confusing people with that. Unfortunately, I think that displaying
HEAP_XMIN_FROZEN will cause even more confusion. I don't think that
HEAP_XMIN_FROZEN is an abstraction at all. It's a notational
convenience.
Well, *I* think that HEAP_XMIN_FROZEN is an abstraction. That's why
we have #define -- to help us create abstractions.
I don't think it's our place to "interpret" the bits. Are we *also*
going to show HEAP_XMIN_FROZEN when xmin is physically set to
FrozenTransactionId?
No, of course not. We're talking about how to display the 256 and 512
bits of t_infomask. Those have four states: nothing, committed,
invalid, frozen. You're arguing that frozen isn't a real state, that
it's somehow just a combination of committed and invalid, but I think
that's the wrong way of thinking about it. When the 256-bit is clear,
the 512-bit tells you whether the xmin is known invalid, but when the
256-bit is set, the 512-bit tells you whether the tuple is also
frozen.
Before HEAP_XMIN_FROZEN existed, it would have been right to display
the state where both bits are set as committed|invalid, because that
would clearly show you that two things had been set that should never
both be set at the same time. But now that's a valid state with a
well-defined meaning and I think we should display the actual meaning
of that state.
Where does it end?
I guess it ends wherever we decide to stop. This isn't some kind of
crazy slippery slope we're talking about here, where one day we're
labeling informask bits and the next day it's global thermonuclear
war.
I think that we should prominently document that HEAP_XMIN_COMMITTED
|HEAP_XMIN_ABORTED == HEAP_XMIN_FROZEN, rather than trying to hide
complexity that we have no business hiding in a tool like pageinspect.
I respect that opinion, but I don't think I'm trying to hide anything.
I think I'm proposing that we display the information in what I
believed to be the clearest and most accurate way.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, Oct 13, 2017 at 1:02 PM, Robert Haas <robertmhaas@gmail.com> wrote:
I don't think it's our place to "interpret" the bits. Are we *also*
going to show HEAP_XMIN_FROZEN when xmin is physically set to
FrozenTransactionId?No, of course not. We're talking about how to display the 256 and 512
bits of t_infomask. Those have four states: nothing, committed,
invalid, frozen. You're arguing that frozen isn't a real state, that
it's somehow just a combination of committed and invalid, but I think
that's the wrong way of thinking about it.
No, I'm arguing that they're just bits. Show the bits, rather than
interpreting what is displayed. Document that there are other logical
states that are represented as composites of contradictory/mutually
exclusive states.
Anyone who hopes to interpret these values has to be an expert anyway,
or willing to become something of an expert. There is a good chance
that they've taken an interest because something is already wrong.
Before HEAP_XMIN_FROZEN existed, it would have been right to display
the state where both bits are set as committed|invalid, because that
would clearly show you that two things had been set that should never
both be set at the same time. But now that's a valid state with a
well-defined meaning and I think we should display the actual meaning
of that state.Where does it end?
I guess it ends wherever we decide to stop.
You can take what you're saying much further. What about
HEAP_XMAX_SHR_LOCK, and HEAP_MOVED? Code like HEAP_LOCKED_UPGRADED()
pretty strongly undermines the idea that these composite values are
abstractions.
I think that we should prominently document that HEAP_XMIN_COMMITTED
|HEAP_XMIN_ABORTED == HEAP_XMIN_FROZEN, rather than trying to hide
complexity that we have no business hiding in a tool like pageinspect.I respect that opinion, but I don't think I'm trying to hide anything.
I think I'm proposing that we display the information in what I
believed to be the clearest and most accurate way.
pg_filedump doesn't display HEAP_XMIN_FROZEN, either. (Nor does it
ever display any of the other composite t_infomask/t_infomask2
values.)
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, Oct 13, 2017 at 4:36 PM, Peter Geoghegan <pg@bowt.ie> wrote:
No, I'm arguing that they're just bits. Show the bits, rather than
interpreting what is displayed. Document that there are other logical
states that are represented as composites of contradictory/mutually
exclusive states.
/me shrugs.
I think it's perfectly sensible to view those 2 bits as making up a
2-bit field with 4 states rather than displaying each bit
individually, but you obviously disagree. Fair enough.
I guess it ends wherever we decide to stop.
You can take what you're saying much further. What about
HEAP_XMAX_SHR_LOCK, and HEAP_MOVED? Code like HEAP_LOCKED_UPGRADED()
pretty strongly undermines the idea that these composite values are
abstractions.
HEAP_MOVED is obviously a different kind of thing. The combination of
both bits has no meaning distinct from the meaning of the individual
bits; in fact, I think it's a shouldn't-happen state. Not sure about
HEAP_XMAX_SHR_LOCK.
pg_filedump doesn't display HEAP_XMIN_FROZEN, either. (Nor does it
ever display any of the other composite t_infomask/t_infomask2
values.)
I can think of two possible explanations for that. Number one, the
tool was written before HEAP_XMIN_FROZEN was invented and hasn't been
updated for those changes. Number two, the author of the tool agrees
with your position rather than mine.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sat, Oct 14, 2017 at 10:58 AM, Robert Haas <robertmhaas@gmail.com> wrote:
I think it's perfectly sensible to view those 2 bits as making up a
2-bit field with 4 states rather than displaying each bit
individually, but you obviously disagree. Fair enough.
I guess it is that simple.
I can think of two possible explanations for that. Number one, the
tool was written before HEAP_XMIN_FROZEN was invented and hasn't been
updated for those changes.
Have we invented our last t_infomask/t_infomask2 (logical) status already?
Number two, the author of the tool agrees
with your position rather than mine.
I am working on an experimental version of pg_filedump, customized to
output XML that can be interpreted by an open source hex editor. The
XML makes the hex editor produce color coded, commented
tags/annotations for any given heap or B-Tree relation. This includes
tooltips with literal values for all status bits (including
t_infomask/t_infomask2 bits, IndexTuple bits, B-Tree meta page status
bits, PD_* page-level bits, ItemId bits, and others). I tweeted about
this several months ago, when it was just a tool I wrote for myself,
and received a surprisingly positive response. It seems like I'm on to
something, and should release the tool to the community.
I mention this project because it very much informs my perspective
here. Having spent quite a while deliberately corrupting test data in
novel ways, just to see what happens, the "work backwards from the
storage format" perspective feels very natural to me. I do think that
I understand where you're coming from too, though.
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 10/14/2017 11:47 PM, Peter Geoghegan wrote:
On Sat, Oct 14, 2017 at 10:58 AM, Robert Haas <robertmhaas@gmail.com> wrote:
I think it's perfectly sensible to view those 2 bits as making up a
2-bit field with 4 states rather than displaying each bit
individually, but you obviously disagree. Fair enough.>I guess it is that simple.
FWIW, my opinion falls in line with Robert's.
Also, whichever way it goes, this is a patch I've been wanting for a
long time.
--
Vik Fearing +33 6 46 75 15 36
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sat, Oct 14, 2017 at 2:47 PM, Peter Geoghegan <pg@bowt.ie> wrote:
I am working on an experimental version of pg_filedump, customized to
output XML that can be interpreted by an open source hex editor. The
XML makes the hex editor produce color coded, commented
tags/annotations for any given heap or B-Tree relation. This includes
tooltips with literal values for all status bits (including
t_infomask/t_infomask2 bits, IndexTuple bits, B-Tree meta page status
bits, PD_* page-level bits, ItemId bits, and others).
This is now available from: https://github.com/petergeoghegan/pg_hexedit
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Sep 14, 2017 at 11:00 PM, Craig Ringer <craig@2ndquadrant.com> wrote:
On 14 September 2017 at 19:57, Ashutosh Sharma <ashu.coek88@gmail.com>
wrote:Are you planning to work on the review comments from Robert, Moon
Insung and supply the new patch. I just had a quick glance into this
mail thread (after a long time) and could understand Robert's concern
till some extent. I think, he is trying to say that if a tuple is
frozen (committed|invalid) then it shouldn't be shown as COMMITTED and
INVALID together in fact it should just be displayed as FROZEN tuple.Yes, I'd like to, and should have time for it in this CF.
My plan is to emit raw flags by default, so FROZEN would't be shown at all,
only COMMITTED|INVALID. If the bool to decode combined flags is set, then
it'll show things like FROZEN, and hide COMMITTED|INVALID. Similar for other
combos.
FWIW, I agree with this direction. ISTM the showing the raw flags by
default and having an option to show combined flags would be a right
way.
I sometimes wanted to have the same mechanism for lp_flags but maybe
it should be discussed on a separated thread.
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
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 ...
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Thu, Aug 22, 2019 at 12:19 AM Alvaro Herrera
<alvherre@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.
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Thu, Aug 22, 2019 at 12:36:10AM +0900, Masahiko Sawada wrote:
I will update the patch and register to the next Commit Fest tomorrow
if nobody is interested in.
Thanks, Sawada-san.
--
Michael
On Thu, Aug 22, 2019 at 12:36 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
On Thu, Aug 22, 2019 at 12:19 AM Alvaro Herrera
<alvherre@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
Attachments:
doc_new_section_for_heap_funcs.patchapplication/x-patch; name=doc_new_section_for_heap_funcs.patchDownload
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>
v3-0001-Introduce-heap_infomask_flags-to-decode-infomask-.patchapplication/x-patch; name=v3-0001-Introduce-heap_infomask_flags-to-decode-infomask-.patchDownload
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
On Fri, Aug 23, 2019 at 11:09:44AM +0900, Masahiko Sawada wrote:
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.
I agree with the new heap section, and your patch on that looks good.
While on it, I have one suggestion: fsm_page_contents does not have an
example of query. Could we add one while on it? An example
consistent with the other function's examples:
=# SELECT fsm_page_contents(get_raw_page('pg_class', 'fsm', 0));
--
Michael
On Fri, Aug 23, 2019 at 12:27 PM Michael Paquier <michael@paquier.xyz> wrote:
On Fri, Aug 23, 2019 at 11:09:44AM +0900, Masahiko Sawada wrote:
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.I agree with the new heap section, and your patch on that looks good.
While on it, I have one suggestion: fsm_page_contents does not have an
example of query. Could we add one while on it? An example
consistent with the other function's examples:
=# SELECT fsm_page_contents(get_raw_page('pg_class', 'fsm', 0));
Good idea. I've updated the doc update patch.
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Attachments:
new_section_for_heap_doc_v2.patchapplication/x-patch; name=new_section_for_heap_doc_v2.patchDownload
diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml
index 8d81f88..e1753aa 100644
--- a/doc/src/sgml/pageinspect.sgml
+++ b/doc/src/sgml/pageinspect.sgml
@@ -127,6 +127,55 @@ 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. For example:
+<screen>
+test=# SELECT fsm_page_contents(get_raw_page('pg_class', 'fsm', 0));
+ fsm_page_contents
+-------------------
+ 0: 235 +
+ 1: 235 +
+ 3: 235 +
+ 7: 235 +
+ 15: 235 +
+ 31: 235 +
+ 63: 235 +
+ 127: 235 +
+ 255: 235 +
+ 511: 235 +
+ 1023: 235 +
+ 2047: 235 +
+ 4095: 235 +
+ fp_next_slot: 0 +
+</screen>
+ 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 +252,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>
On Fri, Aug 23, 2019 at 03:35:01PM +0900, Masahiko Sawada wrote:
Good idea. I've updated the doc update patch.
Thanks. I have removed the output part as I am not sure that it is
that helpful for the reader, and applied it down to v10 where the
sections for function types have been introduced (see b5e3942). It
felt also more natural to move the description of the output after
giving the query.
--
Michael
On Fri, Aug 23, 2019 at 8:44 PM Michael Paquier <michael@paquier.xyz> wrote:
On Fri, Aug 23, 2019 at 03:35:01PM +0900, Masahiko Sawada wrote:
Good idea. I've updated the doc update patch.
Thanks. I have removed the output part as I am not sure that it is
that helpful for the reader, and applied it down to v10 where the
sections for function types have been introduced (see b5e3942). It
felt also more natural to move the description of the output after
giving the query.
Thank you!
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
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
Attachments:
v3-0001-Introduce-heap_infomask_flags-to-decode-infomask-.patchtext/x-diff; charset=us-asciiDownload
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
On 2019-Sep-04, Alvaro Herrera wrote:
Attached v3 again, for CFbot's benefit. No changes from last time.
According to CFbot, the Windows build fails with this patch. Please
fix.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Wed, Sep 04, 2019 at 04:50:45PM -0400, Alvaro Herrera wrote:
According to CFbot, the Windows build fails with this patch. Please
fix.
To save a couple of clicks:
"C:\projects\postgresql\pageinspect.vcxproj" (default target) (56) ->
(Link target) ->
heapfuncs.obj : error LNK2001: unresolved external symbol
pg_popcount32 [C:\projects\postgresql\pageinspect.vcxproj]
.\Release\pageinspect\pageinspect.dll : fatal error LNK1120: 1
unresolved externals [C:\projects\postgresql\pageinspect.vcxproj]
I think that it would be more simple to just use pg_popcount().
That's what other contrib modules do (for example ltree or intarray).
--
Michael
On Thu, Sep 5, 2019 at 10:41 AM Michael Paquier <michael@paquier.xyz> wrote:
On Wed, Sep 04, 2019 at 04:50:45PM -0400, Alvaro Herrera wrote:
According to CFbot, the Windows build fails with this patch. Please
fix.To save a couple of clicks:
"C:\projects\postgresql\pageinspect.vcxproj" (default target) (56) ->
(Link target) ->
heapfuncs.obj : error LNK2001: unresolved external symbol
pg_popcount32 [C:\projects\postgresql\pageinspect.vcxproj]
.\Release\pageinspect\pageinspect.dll : fatal error LNK1120: 1
unresolved externals [C:\projects\postgresql\pageinspect.vcxproj]I think that it would be more simple to just use pg_popcount().
That's what other contrib modules do (for example ltree or intarray).
Thanks. I hope the attached new patch fixes this issue.
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Attachments:
v4-0001-Introduce-heap_infomask_flags-to-decode-infomask-.patchapplication/octet-stream; name=v4-0001-Introduce-heap_infomask_flags-to-decode-infomask-.patchDownload
From 1f1108881de4a95d870073ead4dc97d8d041c0e1 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 v4] Introduce heap_infomask_flags to decode infomask and
infomask2
---
contrib/pageinspect/Makefile | 2 +-
contrib/pageinspect/expected/page.out | 97 +++++++++++++++++++++++
contrib/pageinspect/heapfuncs.c | 106 ++++++++++++++++++++++++++
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, 277 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..241cc8f 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,108 @@ 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_popcount((const char *) &t_infomask, sizeof(uint16)) +
+ pg_popcount((const char *) &t_infomask2, sizeof(uint16));
+
+ 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 cb08c76..4ae8038 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_infomask_flags</function> function can be used to unpack the
+ recognised bits of the infomasks of heap tuples.
+ </para>
</listitem>
</varlistentry>
@@ -236,6 +240,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
On Thu, Sep 5, 2019 at 2:17 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
Thanks. I hope the attached new patch fixes this issue.
*
+-- 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'
Isn't it better to name this function as tuple_infomask_flags or
something like that? The other functions that start their name with
heap take page as input. Also, I think the index-related functions
that start with index name take blk/page as input.
*
+ <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>
I think it is better to use one example for this function as we do for
other functions in this doc.
*
+ <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>
decode_combined should use parameter marker (like
<parameter>decode_combined</parameter>). Instead of saying
"decode_combined" is set, you can use true/false terminology as that
is what we use for this parameter. See explanation of "do_detoast"
that is used in function tuple_data_split in the same doc
(pageinspect.sgml) for reference.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Sun, Sep 8, 2019 at 1:06 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Thu, Sep 5, 2019 at 2:17 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
Thanks. I hope the attached new patch fixes this issue.
Some more comments:
*
+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);
Do we really need a LATERAL clause in the above kind of queries?
AFAIU, the functions can reference the columns that exist in the FROM
list, but I might be missing some point.
*
+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_popcount((const char *) &t_infomask, sizeof(uint16)) +
+ pg_popcount((const char *) &t_infomask2, sizeof(uint16));
All the functions in this file are allowed only for superusers and
there is an explanation for the same as mentioned in the file header
comments. Is there a reason for this function to be different?
I think one possible explanation could be that here we are not passing
raw page on which this function will operate on, but isn't the same
true for tuple_data_split?
In any case, if you think that this function needs to behave
differently w.r.t superuser privileges, then it is better to add some
comments in the function header to explain the same.
*
+Datum
+heap_infomask_flags(PG_FUNCTION_ARGS)
{
..
+
+ d = (Datum *) palloc0(sizeof(Datum) * bitcnt);
It seems we don't free this memory before leaving this function. I
think it shouldn't be a big problem as this will be normally allocated
in ExprContext and shouldn't last for long, but if there is no strong
reason, I think it is better to free it. You can find the examples in
code both where we free after such usage and where we don't. I prefer
to free it.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On 2019-Sep-08, Amit Kapila wrote:
On Thu, Sep 5, 2019 at 2:17 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
Thanks. I hope the attached new patch fixes this issue.
* +-- 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'Isn't it better to name this function as tuple_infomask_flags or
something like that? The other functions that start their name with
heap take page as input. Also, I think the index-related functions
that start with index name take blk/page as input.
I think that other table AMs are not necessarily going to use the same
infomask flags, so I think we should keep a name that is somehow
heapam-specific. Maybe "heapam_infomask_flags" would be okay?
+ <function>heap_infomask_flags(infomask integer, infomask2
integer, decode_combined bool) returns text[]</function>
I think it is better to use one example for this function as we do for
other functions in this doc.
Agreed.
+ <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>decode_combined should use parameter marker (like
<parameter>decode_combined</parameter>). Instead of saying
"decode_combined" is set, you can use true/false terminology as that
is what we use for this parameter.
Agreed.
Some more comments: * +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);Do we really need a LATERAL clause in the above kind of queries?
AFAIU, the functions can reference the columns that exist in the FROM
list, but I might be missing some point.
I think the spec allows the LATERAL keyword to be implicit in the case
of functions, so this seems a matter of style. Putting LATERAL
explicitly seems slightly clearer, to me.
+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);
All the functions in this file are allowed only for superusers and
there is an explanation for the same as mentioned in the file header
comments. Is there a reason for this function to be different?
The other functions can crash if fed arbitrary input. I don't think
this one can crash, so it seems okay for it not to be superuser-only.
In any case, if you think that this function needs to behave
differently w.r.t superuser privileges, then it is better to add some
comments in the function header to explain the same.
Yeah.
* +Datum +heap_infomask_flags(PG_FUNCTION_ARGS) { .. + + d = (Datum *) palloc0(sizeof(Datum) * bitcnt);It seems we don't free this memory before leaving this function. I
think it shouldn't be a big problem as this will be normally allocated
in ExprContext and shouldn't last for long, but if there is no strong
reason, I think it is better to free it.
Agreed.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Import Notes
Reply to msg id not found: CAA4eK1LmpXd6d+getjPGxa74M_DZ+BD2PA8-LU-cOXfDEMJB5A@mail.gmail.comCAA4eK1LPvgUS3ZJ9vsLQ7qsoEWpZsD5fwyzCsgMHkBcu+6-XAQ@mail.gmail.com | Resolved by subject fallback
On Mon, Sep 9, 2019 at 6:22 PM Alvaro Herrera from 2ndQuadrant
<alvherre@alvh.no-ip.org> wrote:
On 2019-Sep-08, Amit Kapila wrote:
On Thu, Sep 5, 2019 at 2:17 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
Thanks. I hope the attached new patch fixes this issue.
* +-- 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'Isn't it better to name this function as tuple_infomask_flags or
something like that? The other functions that start their name with
heap take page as input. Also, I think the index-related functions
that start with index name take blk/page as input.I think that other table AMs are not necessarily going to use the same
infomask flags, so I think we should keep a name that is somehow
heapam-specific. Maybe "heapam_infomask_flags" would be okay?
It will look bit strange to use heapam as a prefix for this function
when all others use heap. I guess if we want to keep it AM specific,
then the proposed name (heap_infomask_flags) is better or
alternatively we can consider heap_tuple_infomask_flags?
Some more comments: * +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);Do we really need a LATERAL clause in the above kind of queries?
AFAIU, the functions can reference the columns that exist in the FROM
list, but I might be missing some point.I think the spec allows the LATERAL keyword to be implicit in the case
of functions, so this seems a matter of style. Putting LATERAL
explicitly seems slightly clearer, to me.
No problem.
+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);All the functions in this file are allowed only for superusers and
there is an explanation for the same as mentioned in the file header
comments. Is there a reason for this function to be different?The other functions can crash if fed arbitrary input. I don't think
this one can crash, so it seems okay for it not to be superuser-only.
At the beginning of pageinspect documentation page, we have a line
"All of these functions may be used only by superusers.". We need to
change that and then maybe give some explanation of why this
particular function will be allowed to non-superusers. BTW, do you
have any use case in mind for the same because anyway we need
superuser privileges to get the page contents and I think this
function can't be used independently?
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Tue, Sep 10, 2019 at 10:21 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Mon, Sep 9, 2019 at 6:22 PM Alvaro Herrera from 2ndQuadrant
<alvherre@alvh.no-ip.org> wrote:On 2019-Sep-08, Amit Kapila wrote:
On Thu, Sep 5, 2019 at 2:17 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
Thanks. I hope the attached new patch fixes this issue.
* +-- 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'Isn't it better to name this function as tuple_infomask_flags or
something like that? The other functions that start their name with
heap take page as input. Also, I think the index-related functions
that start with index name take blk/page as input.I think that other table AMs are not necessarily going to use the same
infomask flags, so I think we should keep a name that is somehow
heapam-specific. Maybe "heapam_infomask_flags" would be okay?It will look bit strange to use heapam as a prefix for this function
when all others use heap. I guess if we want to keep it AM specific,
then the proposed name (heap_infomask_flags) is better or
alternatively we can consider heap_tuple_infomask_flags?
+1 for heap_tuple_infomask_flags. And do we need to change
tuple_data_split to heap_tuple_data_split as well because it's also a
heap specific function?
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Tue, Sep 10, 2019 at 07:51:08AM +0530, Amit Kapila wrote:
It will look bit strange to use heapam as a prefix for this function
when all others use heap. I guess if we want to keep it AM specific,
then the proposed name (heap_infomask_flags) is better or
alternatively we can consider heap_tuple_infomask_flags?
Using "heap_" as prefix of the function looks like the best choice to
me and that's more consistent with the other functions we have
already. Using "tuple" looks sensible as well so the last name you are
proposing sounds like a good alternative.
At the beginning of pageinspect documentation page, we have a line
"All of these functions may be used only by superusers.". We need to
change that and then maybe give some explanation of why this
particular function will be allowed to non-superusers. BTW, do you
have any use case in mind for the same because anyway we need
superuser privileges to get the page contents and I think this
function can't be used independently?
I would still keep it as superuser-restricted, to avoid any risks with
people playing with the internals of this function. pageinspect is
sensitive enough.
--
Michael
On Tue, Sep 10, 2019 at 8:03 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
On Tue, Sep 10, 2019 at 10:21 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Mon, Sep 9, 2019 at 6:22 PM Alvaro Herrera from 2ndQuadrant
<alvherre@alvh.no-ip.org> wrote:I think that other table AMs are not necessarily going to use the same
infomask flags, so I think we should keep a name that is somehow
heapam-specific. Maybe "heapam_infomask_flags" would be okay?It will look bit strange to use heapam as a prefix for this function
when all others use heap. I guess if we want to keep it AM specific,
then the proposed name (heap_infomask_flags) is better or
alternatively we can consider heap_tuple_infomask_flags?+1 for heap_tuple_infomask_flags. And do we need to change
tuple_data_split to heap_tuple_data_split as well because it's also a
heap specific function?
Good thought, but I think even if we want to change the name of
tuple_data_split, it might be better done separately.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Tue, Sep 10, 2019 at 08:29:43AM +0530, Amit Kapila wrote:
Good thought, but I think even if we want to change the name of
tuple_data_split, it might be better done separately.
Yes, that's not the problem of this patch. Not sure if it actually
makes sense either to change it.
The regression tests added are rather unreadable when it comes to
print a lot of infomask flags. Could you add at least some unnest()
calls to the queries using heap_infomask_flags()? It would make the
diff lookup much more straight-forward to understand.
It would be good to comment as well what 2816 and 1080 stand for. The
current code makes it hard to understand for which purpose this is
used in the tests.
+ If decode_combined is set, combination flags like
Missing a markup here.
I am switching the patch as "waiting on author". Could you address
the comments raised please?
--
Michael
On Wed, Sep 11, 2019 at 1:46 PM Michael Paquier <michael@paquier.xyz> wrote:
On Tue, Sep 10, 2019 at 08:29:43AM +0530, Amit Kapila wrote:
Good thought, but I think even if we want to change the name of
tuple_data_split, it might be better done separately.Yes, that's not the problem of this patch. Not sure if it actually
makes sense either to change it.
Hmm it will be more consistent with other functions but I think we
would need to increase the pageinspect version to 1.8 and need the new
sql file to rename the function name. And it will be for PG12, not
PG13. If we have to do it someday I think it's better to do it in PG12
that the table AM has been introduced to. Anyway I've attached
separate patch for it.
The regression tests added are rather unreadable when it comes to
print a lot of infomask flags. Could you add at least some unnest()
calls to the queries using heap_infomask_flags()? It would make the
diff lookup much more straight-forward to understand.
Seems good idea.
It would be good to comment as well what 2816 and 1080 stand for. The
current code makes it hard to understand for which purpose this is
used in the tests.
I've reconsidered and updated the regression tests.
+ If decode_combined is set, combination flags like
Missing a markup here.
Fixed.
I've attached the updated patch that incorporated all comments. I kept
the function as superuser-restricted.
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Attachments:
rename_to_heap_tuple_data_split.patchtext/x-patch; charset=US-ASCII; name=rename_to_heap_tuple_data_split.patchDownload
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/pageinspect--1.7--1.8.sql b/contrib/pageinspect/pageinspect--1.7--1.8.sql
new file mode 100644
index 0000000000..39421e5699
--- /dev/null
+++ b/contrib/pageinspect/pageinspect--1.7--1.8.sql
@@ -0,0 +1,6 @@
+/* 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
+
+ALTER FUNCTION tuple_data_split(oid, bytea, integer, integer, text) RENAME TO heap_tuple_data_split;
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..bfc3a3fdc1 100644
--- a/contrib/pageinspect/sql/page.sql
+++ b/contrib/pageinspect/sql/page.sql
@@ -26,7 +26,7 @@ SELECT pagesize, version FROM page_header(get_raw_page('test1', 0));
SELECT page_checksum(get_raw_page('test1', 0), 0) IS NOT NULL AS silly_checksum_test;
-SELECT tuple_data_split('test1'::regclass, t_data, t_infomask, t_infomask2, t_bits)
+SELECT heap_tuple_data_split('test1'::regclass, t_data, t_infomask, t_infomask2, t_bits)
FROM heap_page_items(get_raw_page('test1', 0));
SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0));
@@ -49,6 +49,6 @@ drop table test_partitioned;
create table test8 (f1 int, f2 int, f3 int, f4 int, f5 int, f6 int, f7 int, f8 int);
insert into test8(f1, f8) values (x'7f00007f'::int, 0);
select t_bits, t_data from heap_page_items(get_raw_page('test8', 0));
-select tuple_data_split('test8'::regclass, t_data, t_infomask, t_infomask2, t_bits)
+select heap_tuple_data_split('test8'::regclass, t_data, t_infomask, t_infomask2, t_bits)
from heap_page_items(get_raw_page('test8', 0));
drop table test8;
diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml
index 7a767b25ea..29fc32ab95 100644
--- a/doc/src/sgml/pageinspect.sgml
+++ b/doc/src/sgml/pageinspect.sgml
@@ -189,17 +189,17 @@ test=# SELECT * FROM heap_page_items(get_raw_page('pg_class', 0));
<varlistentry>
<term>
- <function>tuple_data_split(rel_oid oid, t_data bytea, t_infomask integer, t_infomask2 integer, t_bits text [, do_detoast bool]) returns bytea[]</function>
+ <function>heap_tuple_data_split(rel_oid oid, t_data bytea, t_infomask integer, t_infomask2 integer, t_bits text [, do_detoast bool]) returns bytea[]</function>
<indexterm>
- <primary>tuple_data_split</primary>
+ <primary>heap_tuple_data_split</primary>
</indexterm>
</term>
<listitem>
<para>
- <function>tuple_data_split</function> splits tuple data into attributes
+ <function>heap_tuple_data_split</function> splits tuple data into attributes
in the same way as backend internals.
<screen>
-test=# SELECT tuple_data_split('pg_class'::regclass, t_data, t_infomask, t_infomask2, t_bits) FROM heap_page_items(get_raw_page('pg_class', 0));
+test=# SELECT heap_tuple_data_split('pg_class'::regclass, t_data, t_infomask, t_infomask2, t_bits) 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>.
v5-0001-Introduce-heap_tuple_infomask_flags-to-decode-t_i.patchtext/x-patch; charset=US-ASCII; name=v5-0001-Introduce-heap_tuple_infomask_flags-to-decode-t_i.patchDownload
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
On 2019-Sep-11, Masahiko Sawada wrote:
On Wed, Sep 11, 2019 at 1:46 PM Michael Paquier <michael@paquier.xyz> wrote:
On Tue, Sep 10, 2019 at 08:29:43AM +0530, Amit Kapila wrote:
Good thought, but I think even if we want to change the name of
tuple_data_split, it might be better done separately.Yes, that's not the problem of this patch. Not sure if it actually
makes sense either to change it.Hmm it will be more consistent with other functions but I think we
would need to increase the pageinspect version to 1.8 and need the new
sql file to rename the function name. And it will be for PG12, not
PG13. If we have to do it someday I think it's better to do it in PG12
that the table AM has been introduced to. Anyway I've attached
separate patch for it.
I'd rather not change the name of the existing function ... that
function is pretty old (it was introduced in 9.6, commit d6061f83a166).
I think we can regard that name as an historical accident, and use a
modern name convention for the new function (and any hypothetical future
ones) that will, sadly, collide with the historical name for the old
function.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Wed, Sep 11, 2019 at 11:30 PM Alvaro Herrera from 2ndQuadrant
<alvherre@alvh.no-ip.org> wrote:
On 2019-Sep-11, Masahiko Sawada wrote:
On Wed, Sep 11, 2019 at 1:46 PM Michael Paquier <michael@paquier.xyz> wrote:
On Tue, Sep 10, 2019 at 08:29:43AM +0530, Amit Kapila wrote:
Good thought, but I think even if we want to change the name of
tuple_data_split, it might be better done separately.Yes, that's not the problem of this patch. Not sure if it actually
makes sense either to change it.Hmm it will be more consistent with other functions but I think we
would need to increase the pageinspect version to 1.8 and need the new
sql file to rename the function name. And it will be for PG12, not
PG13. If we have to do it someday I think it's better to do it in PG12
that the table AM has been introduced to. Anyway I've attached
separate patch for it.I'd rather not change the name of the existing function ... that
function is pretty old (it was introduced in 9.6, commit d6061f83a166).
I think we can regard that name as an historical accident, and use a
modern name convention for the new function (and any hypothetical future
ones) that will, sadly, collide with the historical name for the old
function.
Okay, that makes sense.
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Wed, Sep 11, 2019 at 8:53 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
I've attached the updated patch that incorporated all comments. I kept
the function as superuser-restricted.
Thanks for the updated patch.
Few more comments:
*
+ if (!superuser())
+ ereport(ERROR,
+ (errcode
(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to use raw
page functions")));
I think it is better to use a message like "must be superuser to use
pageinspect functions" as this function doesn't take raw page as
input. If you see other functions like bt_page_items which doesn't
take raw page as input has the message which I am suggesting. I can
see that tuple_data_split also has a similar message as you are
proposing, but I think that is also not very appropriate.
*
else
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask))
+ d[cnt++] = CStringGetTextDatum
("HEAP_XMAX_LOCK_ONLY");
If the idea is that whenever decode_combined flag is false, we will
display the raw flags set on the tuple, then why to try to interpret
flags on a tuple in the above case.
*
+ 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");
+ }
I am not completely sure whether we want to cover HEAP_LOCKED_UPGRADED
case even when decode_combined flag is set. It seems this is a bit
more interpretation of flags than we are doing in other cases. For
example, other cases like HEAP_XMAX_SHR_LOCK or HEAP_XMIN_FROZEN are
the flags that are explicitly set on the tuple so displaying them
makes sense, but the same is not true for HEAP_LOCKED_UPGRADED.
*
+CREATE FUNCTION heap_tuple_infomask_flags(
+ t_infomask integer,
+ t_infomask2 integer,
+ decode_combined boolean DEFAULT false)
I am not very happy with the parameter name 'decode_combined'. It is
not clear from the name what it means and I think it doesn't even
match with what we are actually doing here. How about raw_flags,
raw_tuple_flags or something like that?
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Wed, Sep 11, 2019 at 11:22:45PM +0800, Masahiko Sawada wrote:
Hmm it will be more consistent with other functions but I think we
would need to increase the pageinspect version to 1.8 and need the new
sql file to rename the function name. And it will be for PG12, not
PG13. If we have to do it someday I think it's better to do it in PG12
that the table AM has been introduced to. Anyway I've attached
separate patch for it.
Like Alvaro, I would discard this one for now.
I've attached the updated patch that incorporated all comments. I kept
the function as superuser-restricted.
But not this one. So committed. I have gone through the patch and
adjusted a couple of things in the tests, the docs with weird
formulations and an example leading mainly to NULLs returned when
scanning the first page of pg_class. The tests needed some
improvements to gain in clarity (no need for unnest with 2 elements,
added tests for all the combined flags, etc.). The patch was not
indented either but this is no big deal.
I hope I forgot to credit nobody in the commit message. If that's the
case, you are the winner of a drink of your choice the next time we
meet.
--
Michael
On Thu, Sep 12, 2019 at 11:43 AM Michael Paquier <michael@paquier.xyz> wrote:
On Wed, Sep 11, 2019 at 11:22:45PM +0800, Masahiko Sawada wrote:
Hmm it will be more consistent with other functions but I think we
would need to increase the pageinspect version to 1.8 and need the new
sql file to rename the function name. And it will be for PG12, not
PG13. If we have to do it someday I think it's better to do it in PG12
that the table AM has been introduced to. Anyway I've attached
separate patch for it.Like Alvaro, I would discard this one for now.
I've attached the updated patch that incorporated all comments. I kept
the function as superuser-restricted.But not this one. So committed.
I had a few comments as posted in the previous email which I think we
can address incrementally as the patch for those is produced.
However, one point which I am slightly worried is the last one in my
email. Are we happy with the name of the new parameter in the API
decode_combined? Because if we decide to change that then we need to
change the exposed API and I think in the ideal case we need to change
the version as well, but I might be wrong and maybe the parameter name
as committed is good enough in which case we should be good.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Thu, Sep 12, 2019 at 1:56 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
On Wed, Sep 11, 2019 at 8:53 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
I've attached the updated patch that incorporated all comments. I kept
the function as superuser-restricted.Thanks for the updated patch.
Few more comments:
Thank you for your comments.
* + if (!superuser()) + ereport(ERROR, + (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions")));I think it is better to use a message like "must be superuser to use
pageinspect functions" as this function doesn't take raw page as
input. If you see other functions like bt_page_items which doesn't
take raw page as input has the message which I am suggesting. I can
see that tuple_data_split also has a similar message as you are
proposing, but I think that is also not very appropriate.
Agreed. Will fix.
* else + { + if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask)) + d[cnt++] = CStringGetTextDatum ("HEAP_XMAX_LOCK_ONLY");If the idea is that whenever decode_combined flag is false, we will
display the raw flags set on the tuple, then why to try to interpret
flags on a tuple in the above case.
Hmm my understanding of 'decode_combined' is to decode the flags that
we represent by using multiple flags. HEAP_XMAX_IS_LOCKED_ONLY is true
either if HEAP_XMAX_LOCK_ONLY is set or not a multi and the EXCL_LOCK
bit is set. That is it requires only one flag. So I thought that it's
not a combined flag.
* + 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"); + }I am not completely sure whether we want to cover HEAP_LOCKED_UPGRADED
case even when decode_combined flag is set. It seems this is a bit
more interpretation of flags than we are doing in other cases. For
example, other cases like HEAP_XMAX_SHR_LOCK or HEAP_XMIN_FROZEN are
the flags that are explicitly set on the tuple so displaying them
makes sense, but the same is not true for HEAP_LOCKED_UPGRADED.
I thought it would be better to interpret it as much as possible,
especially for diagnostic use cases. I'm concerned that user might not
be able to get enough information for investigation if we
intentionally filtered particular flags.
* +CREATE FUNCTION heap_tuple_infomask_flags( + t_infomask integer, + t_infomask2 integer, + decode_combined boolean DEFAULT false)I am not very happy with the parameter name 'decode_combined'. It is
not clear from the name what it means and I think it doesn't even
match with what we are actually doing here. How about raw_flags,
raw_tuple_flags or something like that?
raw_flags might be more straightforward. Or perhaps the third idea
could be show_raw_flags? If other hackers agree to change the flag
name I'll fix it.
I'll submit the patch to fix the commit after we got a consensus on
the above changes.
Regards,
--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Thu, Sep 12, 2019 at 05:34:08PM +0800, Masahiko Sawada wrote:
On Thu, Sep 12, 2019 at 1:56 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
I think it is better to use a message like "must be superuser to use
pageinspect functions" as this function doesn't take raw page as
input. If you see other functions like bt_page_items which doesn't
take raw page as input has the message which I am suggesting. I can
see that tuple_data_split also has a similar message as you are
proposing, but I think that is also not very appropriate.Agreed. Will fix.
Well, those functions are all able to work only from data of a raw
page, so the existing message was actually fine by me. If you want to
change it this way, I don't see either any arguments against.
Hmm my understanding of 'decode_combined' is to decode the flags that
we represent by using multiple flags. HEAP_XMAX_IS_LOCKED_ONLY is true
either if HEAP_XMAX_LOCK_ONLY is set or not a multi and the EXCL_LOCK
bit is set. That is it requires only one flag. So I thought that it's
not a combined flag.
Same interpretation here.
I am not completely sure whether we want to cover HEAP_LOCKED_UPGRADED
case even when decode_combined flag is set. It seems this is a bit
more interpretation of flags than we are doing in other cases. For
example, other cases like HEAP_XMAX_SHR_LOCK or HEAP_XMIN_FROZEN are
the flags that are explicitly set on the tuple so displaying them
makes sense, but the same is not true for HEAP_LOCKED_UPGRADED.I thought it would be better to interpret it as much as possible,
especially for diagnostic use cases. I'm concerned that user might not
be able to get enough information for investigation if we
intentionally filtered particular flags.
For HEAP_LOCKED_UPGRADED, my interpretation was that the current code
is correct to understand it as a decomposition of HEAP_XMAX_IS_MULTI
and HEAP_XMAX_LOCK_ONLY, still...
It seems to me that the way we define combined flags is subject to
a lot of interpretation. Honestly, if we cannot come up with a clear
definition of what should be combined or not, I would be of the
opinion to just wipe out the option, and just return in the text array
the bits which are set. It has been discussed on the thread that it
would be confusing to not show combined flags to some users as some
bits set have rather contrary meaning when set with others. We tell
the user that all the flag details are defined in htup_details.h in
the code and the documentation so the information is not in the
returned data, but in the code. And I would like to think that users
of pageinspect are knowledgeable enough about Postgres that they would
likely never use decode_combined = true. Likely I am outnumbered
regarding this point, so I won't push hard on it, still I get that the
confusion does not come from this module, but in the way the code
combines and names all the bits for the infomasks :)
And there would be the argument to not use HEAP_XMAX_IS_LOCKED_ONLY()
in the code.
I am not very happy with the parameter name 'decode_combined'. It is
not clear from the name what it means and I think it doesn't even
match with what we are actually doing here. How about raw_flags,
raw_tuple_flags or something like that?raw_flags might be more straightforward. Or perhaps the third idea
could be show_raw_flags? If other hackers agree to change the flag
name I'll fix it.I'll submit the patch to fix the commit after we got a consensus on
the above changes.
decode_combined sounds like a good compromise to me. If there is a
better consensus, well, let's use it, but I don't find those
suggestions to be improvements.
--
Michael
On Thu, Sep 12, 2019 at 4:48 PM Michael Paquier <michael@paquier.xyz> wrote:
On Thu, Sep 12, 2019 at 05:34:08PM +0800, Masahiko Sawada wrote:
On Thu, Sep 12, 2019 at 1:56 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
I think it is better to use a message like "must be superuser to use
pageinspect functions" as this function doesn't take raw page as
input. If you see other functions like bt_page_items which doesn't
take raw page as input has the message which I am suggesting. I can
see that tuple_data_split also has a similar message as you are
proposing, but I think that is also not very appropriate.Agreed. Will fix.
Well, those functions are all able to work only from data of a raw
page, so the existing message was actually fine by me. If you want to
change it this way, I don't see either any arguments against.Hmm my understanding of 'decode_combined' is to decode the flags that
we represent by using multiple flags. HEAP_XMAX_IS_LOCKED_ONLY is true
either if HEAP_XMAX_LOCK_ONLY is set or not a multi and the EXCL_LOCK
bit is set. That is it requires only one flag. So I thought that it's
not a combined flag.Same interpretation here.
Hmm, I thought when decode_combined flag is set to false means we will
display the raw flags set on the tuple without any further
interpretation. I think that is what is most people in thread
advocated about.
I am not completely sure whether we want to cover HEAP_LOCKED_UPGRADED
case even when decode_combined flag is set. It seems this is a bit
more interpretation of flags than we are doing in other cases. For
example, other cases like HEAP_XMAX_SHR_LOCK or HEAP_XMIN_FROZEN are
the flags that are explicitly set on the tuple so displaying them
makes sense, but the same is not true for HEAP_LOCKED_UPGRADED.I thought it would be better to interpret it as much as possible,
especially for diagnostic use cases. I'm concerned that user might not
be able to get enough information for investigation if we
intentionally filtered particular flags.For HEAP_LOCKED_UPGRADED, my interpretation was that the current code
is correct to understand it as a decomposition of HEAP_XMAX_IS_MULTI
and HEAP_XMAX_LOCK_ONLY, still...It seems to me that the way we define combined flags is subject to
a lot of interpretation.
Right.
Honestly, if we cannot come up with a clear
definition of what should be combined or not, I would be of the
opinion to just wipe out the option, and just return in the text array
the bits which are set. It has been discussed on the thread that it
would be confusing to not show combined flags to some users as some
bits set have rather contrary meaning when set with others.
Yes, I think we could have more discussion on this point. It is not
100% clear how we should interpret this flag and or where to draw a
line. It might be that whatever we have done is alright, but still,
it is worth more discussion and opinion from a few more people.
We tell
the user that all the flag details are defined in htup_details.h in
the code and the documentation so the information is not in the
returned data, but in the code. And I would like to think that users
of pageinspect are knowledgeable enough about Postgres that they would
likely never use decode_combined = true. Likely I am outnumbered
regarding this point, so I won't push hard on it, still I get that the
confusion does not come from this module, but in the way the code
combines and names all the bits for the infomasks :)And there would be the argument to not use HEAP_XMAX_IS_LOCKED_ONLY()
in the code.I am not very happy with the parameter name 'decode_combined'. It is
not clear from the name what it means and I think it doesn't even
match with what we are actually doing here. How about raw_flags,
raw_tuple_flags or something like that?raw_flags might be more straightforward. Or perhaps the third idea
could be show_raw_flags? If other hackers agree to change the flag
name I'll fix it.I'll submit the patch to fix the commit after we got a consensus on
the above changes.decode_combined sounds like a good compromise to me. If there is a
better consensus, well, let's use it, but I don't find those
suggestions to be improvements.
I think it depends on the meaning of that flag.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Thu, Sep 12, 2019 at 05:24:17PM +0530, Amit Kapila wrote:
On Thu, Sep 12, 2019 at 4:48 PM Michael Paquier <michael@paquier.xyz> wrote:
Hmm, I thought when decode_combined flag is set to false means we will
display the raw flags set on the tuple without any further
interpretation. I think that is what is most people in thread
advocated about.
Sorry if I created any confusion. When set to false then the raw list
of flags is returned, and that's the default. The example provided in
the docs is careful about that, as well as the description done for
the option (at least I guess so!).
Yes, I think we could have more discussion on this point. It is not
100% clear how we should interpret this flag and or where to draw a
line. It might be that whatever we have done is alright, but still,
it is worth more discussion and opinion from a few more people.
Of course.
decode_combined sounds like a good compromise to me. If there is a
better consensus, well, let's use it, but I don't find those
suggestions to be improvements.I think it depends on the meaning of that flag.
Perhaps using "decode" is the confusing part here? It is more like a
"merge" of the flags, or just a combination of them. An idea that
just popped here would be to name the switch "combine_flags" instead.
--
Michael
On Fri, Sep 13, 2019 at 9:00 AM Michael Paquier <michael@paquier.xyz> wrote:
On Thu, Sep 12, 2019 at 05:24:17PM +0530, Amit Kapila wrote:
On Thu, Sep 12, 2019 at 4:48 PM Michael Paquier <michael@paquier.xyz> wrote:
Hmm, I thought when decode_combined flag is set to false means we will
display the raw flags set on the tuple without any further
interpretation. I think that is what is most people in thread
advocated about.Sorry if I created any confusion. When set to false then the raw list
of flags is returned, and that's the default.
I think that is what we have not done in one of the cases pointed by me.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Fri, Sep 13, 2019 at 09:59:40AM +0530, Amit Kapila wrote:
I think that is what we have not done in one of the cases pointed by me.
Thinking more about it, I see your point now. HEAP_LOCKED_UPGRADED is
not a direct combination of the other flags and depends on other
conditions, so we cannot make a combination of it with other things.
The three others don't have that problem.
Attached is a patch to fix your suggestions. This also removes the
use of HEAP_XMAX_IS_LOCKED_ONLY which did not make completely sense
either as a "raw" flag. While on it, the order of the flags can be
improved to match more the order of htup_details.h
Does this patch address your concerns?
--
Michael
Attachments:
pageinspect-combine-flags.patchtext/x-diff; charset=us-asciiDownload
diff --git a/contrib/pageinspect/expected/page.out b/contrib/pageinspect/expected/page.out
index 6a09d46a57..76f02dbea2 100644
--- a/contrib/pageinspect/expected/page.out
+++ b/contrib/pageinspect/expected/page.out
@@ -91,7 +91,7 @@ FROM heap_page_items(get_raw_page('test1', 0)),
LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(flags);
t_infomask | t_infomask2 | flags
------------+-------------+-----------------------------------------------------------
- 2816 | 2 | {HEAP_XMAX_INVALID,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID}
+ 2816 | 2 | {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID}
(1 row)
-- output the decoded flag HEAP_XMIN_FROZEN instead
@@ -100,7 +100,7 @@ FROM heap_page_items(get_raw_page('test1', 0)),
LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2, true) m(flags);
t_infomask | t_infomask2 | flags
------------+-------------+--------------------------------------
- 2816 | 2 | {HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN}
+ 2816 | 2 | {HEAP_XMIN_FROZEN,HEAP_XMAX_INVALID}
(1 row)
-- tests for decoding of combined flags
@@ -140,20 +140,7 @@ SELECT heap_tuple_infomask_flags(x'C000'::int, 0, true);
SELECT heap_tuple_infomask_flags(x'C000'::int, 0, false);
heap_tuple_infomask_flags
--------------------------------
- {HEAP_MOVED_IN,HEAP_MOVED_OFF}
-(1 row)
-
--- HEAP_LOCKED_UPGRADED = (HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY)
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_LOCKED_UPGRADED}
-(1 row)
-
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, false);
- heap_tuple_infomask_flags
-------------------------------------------
- {HEAP_XMAX_LOCK_ONLY,HEAP_XMAX_IS_MULTI}
+ {HEAP_MOVED_OFF,HEAP_MOVED_IN}
(1 row)
-- test all flags of t_infomask and t_infomask2
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 68f16cd400..c696d7d6d1 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -540,12 +540,8 @@ heap_tuple_infomask_flags(PG_FUNCTION_ARGS)
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");
+ if ((t_infomask & HEAP_XMAX_LOCK_ONLY) != 0)
+ d[cnt++] = CStringGetTextDatum("HEAP_XMAX_LOCK_ONLY");
/* decode combined masks of t_infomaks */
if (decode_combined && (t_infomask & HEAP_XMAX_SHR_LOCK) != 0)
@@ -568,24 +564,23 @@ heap_tuple_infomask_flags(PG_FUNCTION_ARGS)
d[cnt++] = CStringGetTextDatum("HEAP_XMIN_INVALID");
}
+ 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_XMAX_IS_MULTI) != 0)
+ d[cnt++] = CStringGetTextDatum("HEAP_XMAX_IS_MULTI");
+ if ((t_infomask & HEAP_UPDATED) != 0)
+ d[cnt++] = CStringGetTextDatum("HEAP_UPDATED");
+
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");
+ if ((t_infomask & HEAP_MOVED_IN) != 0)
+ d[cnt++] = CStringGetTextDatum("HEAP_MOVED_IN");
}
/* decode t_infomask2 */
diff --git a/contrib/pageinspect/sql/page.sql b/contrib/pageinspect/sql/page.sql
index 0319b5fa11..c04351dba9 100644
--- a/contrib/pageinspect/sql/page.sql
+++ b/contrib/pageinspect/sql/page.sql
@@ -55,9 +55,6 @@ SELECT heap_tuple_infomask_flags(x'0300'::int, 0, false);
-- HEAP_MOVED = (HEAP_MOVED_IN | HEAP_MOVED_OFF)
SELECT heap_tuple_infomask_flags(x'C000'::int, 0, true);
SELECT heap_tuple_infomask_flags(x'C000'::int, 0, false);
--- HEAP_LOCKED_UPGRADED = (HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY)
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, false);
-- test all flags of t_infomask and t_infomask2
SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, false))
On 2019-Sep-13, Michael Paquier wrote:
Attached is a patch to fix your suggestions. This also removes the
use of HEAP_XMAX_IS_LOCKED_ONLY which did not make completely sense
either as a "raw" flag. While on it, the order of the flags can be
improved to match more the order of htup_details.h
A thought I had as I fell asleep last night is to include the derivate
flags in a separate output column altogether. So
heap_tuple_infomask_flags() could be made to return two columns, one
with the straight one-flag-per-bit, and another one with the compound
flags. That way we always have the raw ones available, and we avoid any
confusion about strange cases such as LOCK_UPGRADED and IS_LOCKED_ONLY.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Fri, Sep 13, 2019 at 5:31 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
On 2019-Sep-13, Michael Paquier wrote:
Attached is a patch to fix your suggestions. This also removes the
use of HEAP_XMAX_IS_LOCKED_ONLY which did not make completely sense
either as a "raw" flag. While on it, the order of the flags can be
improved to match more the order of htup_details.hA thought I had as I fell asleep last night is to include the derivate
flags in a separate output column altogether. So
heap_tuple_infomask_flags() could be made to return two columns, one
with the straight one-flag-per-bit, and another one with the compound
flags.
So, in most cases, the compound column will be empty, but in some
cases like HEAP_XMIN_FROZEN, HEAP_XMAX_SHR_LOCK, etc. the flag will be
displayed. I like this idea though it will be a bit of noise in some
cases but it is neat. Another benefit is that one doesn't need to
invoke this function twice to see the compound flags.
That way we always have the raw ones available, and we avoid any
confusion about strange cases such as LOCK_UPGRADED and IS_LOCKED_ONLY.
Yeah, but I am not sure if we want to do display LOCK_UPGRADED stuff
in the compound column as that is not directly comparable to other
flags we want to display there like HEAP_XMIN_FROZEN,
HEAP_XMAX_SHR_LOCK.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Fri, Sep 13, 2019 at 10:42 AM Michael Paquier <michael@paquier.xyz> wrote:
On Fri, Sep 13, 2019 at 09:59:40AM +0530, Amit Kapila wrote:
I think that is what we have not done in one of the cases pointed by me.
Thinking more about it, I see your point now. HEAP_LOCKED_UPGRADED is
not a direct combination of the other flags and depends on other
conditions, so we cannot make a combination of it with other things.
The three others don't have that problem.Attached is a patch to fix your suggestions. This also removes the
use of HEAP_XMAX_IS_LOCKED_ONLY which did not make completely sense
either as a "raw" flag. While on it, the order of the flags can be
improved to match more the order of htup_details.hDoes this patch address your concerns?
Yeah, but I think we should also try to see what we want to do about
'decode_combined' flag-related point, maybe we can adapt to what
Alvaro has purposed?
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Sat, Sep 14, 2019 at 09:51:33AM +0530, Amit Kapila wrote:
Yeah, but I think we should also try to see what we want to do about
'decode_combined' flag-related point, maybe we can adapt to what
Alvaro has purposed?
Thanks, I'll keep note of this patch. I was just going to comment on
the other point raised :)
--
Michael
On Sat, Sep 14, 2019 at 09:25:31AM +0530, Amit Kapila wrote:
On Fri, Sep 13, 2019 at 5:31 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
A thought I had as I fell asleep last night is to include the derivate
flags in a separate output column altogether. So
heap_tuple_infomask_flags() could be made to return two columns, one
with the straight one-flag-per-bit, and another one with the compound
flags.So, in most cases, the compound column will be empty, but in some
cases like HEAP_XMIN_FROZEN, HEAP_XMAX_SHR_LOCK, etc. the flag will be
displayed. I like this idea though it will be a bit of noise in some
cases but it is neat. Another benefit is that one doesn't need to
invoke this function twice to see the compound flags.
Hmmm. Doesn't it become less user-friendly to invoke the function
then? You would need to pass it down to the FROM clause after
fetching the raw page and then parsing its tuple items to have
t_infomask and t_infomask2 passed down as arguments to the new
function. The one-column version has the advantage to be more
consistent with tuple_data_split() after getting all the values parsed
by heap_page_items().
That way we always have the raw ones available, and we avoid any
confusion about strange cases such as LOCK_UPGRADED and IS_LOCKED_ONLY.Yeah, but I am not sure if we want to do display LOCK_UPGRADED stuff
in the compound column as that is not directly comparable to other
flags we want to display there like HEAP_XMIN_FROZEN,
HEAP_XMAX_SHR_LOCK.
Yep, I agree that this one ought to not be considered as a proper
combination. The other three ones are fine though.
--
Michael
On Sat, Sep 14, 2019 at 10:10 AM Michael Paquier <michael@paquier.xyz> wrote:
On Sat, Sep 14, 2019 at 09:25:31AM +0530, Amit Kapila wrote:
On Fri, Sep 13, 2019 at 5:31 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
A thought I had as I fell asleep last night is to include the derivate
flags in a separate output column altogether. So
heap_tuple_infomask_flags() could be made to return two columns, one
with the straight one-flag-per-bit, and another one with the compound
flags.So, in most cases, the compound column will be empty, but in some
cases like HEAP_XMIN_FROZEN, HEAP_XMAX_SHR_LOCK, etc. the flag will be
displayed. I like this idea though it will be a bit of noise in some
cases but it is neat. Another benefit is that one doesn't need to
invoke this function twice to see the compound flags.Hmmm. Doesn't it become less user-friendly to invoke the function
then? You would need to pass it down to the FROM clause after
fetching the raw page and then parsing its tuple items to have
t_infomask and t_infomask2 passed down as arguments to the new
function. The one-column version has the advantage to be more
consistent with tuple_data_split() after getting all the values parsed
by heap_page_items().
Won't 'Lateral' clause be helpful here as the patch contains it in one
of its tests?
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Sat, Sep 14, 2019 at 11:18:37AM +0530, Amit Kapila wrote:
Won't 'Lateral' clause be helpful here as the patch contains it in one
of its tests?
Ah true, I forgot that.
--
Michael
On Sat, Sep 14, 2019 at 03:03:57PM +0900, Michael Paquier wrote:
On Sat, Sep 14, 2019 at 11:18:37AM +0530, Amit Kapila wrote:
Won't 'Lateral' clause be helpful here as the patch contains it in one
of its tests?Ah true, I forgot that.
If we are redesigning the interface, here are two extra thoughts which
may be worth considering:
1) If the function returns multiple columns, could it make sense to
separate infomask and infomask2? This would then give 3 columns:
- The raw flags for infomask.
- The three combined flags for infomask.
- The flags for infomask2.
2) Could it make sense to have a separate function for infomask2?
I'd rather keep everything in a single function, still as we are
discussing the matter..
--
Michael
On Sat, Sep 14, 2019 at 3:00 PM Michael Paquier <michael@paquier.xyz> wrote:
On Sat, Sep 14, 2019 at 03:03:57PM +0900, Michael Paquier wrote:
On Sat, Sep 14, 2019 at 11:18:37AM +0530, Amit Kapila wrote:
Won't 'Lateral' clause be helpful here as the patch contains it in one
of its tests?Ah true, I forgot that.
If we are redesigning the interface, here are two extra thoughts which
may be worth considering:
1) If the function returns multiple columns, could it make sense to
separate infomask and infomask2? This would then give 3 columns:
- The raw flags for infomask.
- The three combined flags for infomask.
- The flags for infomask2.
2) Could it make sense to have a separate function for infomask2?
I don't see much use of separating information for infomask and infomask2.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Mon, Sep 16, 2019 at 11:46:16AM +0530, Amit Kapila wrote:
I don't see much use of separating information for infomask and infomask2.
Okay, using two separate columns leads to the attached. Any thoughts?
This also includes a fix for cases with IS_LOCKED_ONLY and UPGRADED.
--
Michael
Attachments:
pageinspect-combine-flags-2.patchtext/x-diff; charset=us-asciiDownload
diff --git a/contrib/pageinspect/expected/page.out b/contrib/pageinspect/expected/page.out
index 6a09d46a57..c8f6e1b4c6 100644
--- a/contrib/pageinspect/expected/page.out
+++ b/contrib/pageinspect/expected/page.out
@@ -86,80 +86,55 @@ SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0));
-- 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, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(flags);
- t_infomask | t_infomask2 | flags
-------------+-------------+-----------------------------------------------------------
- 2816 | 2 | {HEAP_XMAX_INVALID,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID}
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
+ t_infomask | t_infomask2 | raw_flags | combined_flags
+------------+-------------+-----------------------------------------------------------+--------------------
+ 2816 | 2 | {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID} | {HEAP_XMIN_FROZEN}
(1 row)
-- output the decoded flag HEAP_XMIN_FROZEN instead
-SELECT t_infomask, t_infomask2, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2, true) m(flags);
- t_infomask | t_infomask2 | flags
-------------+-------------+--------------------------------------
- 2816 | 2 | {HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN}
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
+ t_infomask | t_infomask2 | raw_flags | combined_flags
+------------+-------------+-----------------------------------------------------------+--------------------
+ 2816 | 2 | {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID} | {HEAP_XMIN_FROZEN}
(1 row)
-- tests for decoding of combined flags
-- HEAP_XMAX_SHR_LOCK = (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_XMAX_SHR_LOCK}
-(1 row)
-
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, false);
- heap_tuple_infomask_flags
----------------------------------------------
- {HEAP_XMAX_EXCL_LOCK,HEAP_XMAX_KEYSHR_LOCK}
+SELECT * FROM heap_tuple_infomask_flags(x'0050'::int, 0);
+ raw_flags | combined_flags
+---------------------------------------------+----------------------
+ {HEAP_XMAX_KEYSHR_LOCK,HEAP_XMAX_EXCL_LOCK} | {HEAP_XMAX_SHR_LOCK}
(1 row)
-- HEAP_XMIN_FROZEN = (HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID)
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_XMIN_FROZEN}
-(1 row)
-
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, false);
- heap_tuple_infomask_flags
------------------------------------------
- {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID}
+SELECT * FROM heap_tuple_infomask_flags(x'0300'::int, 0);
+ raw_flags | combined_flags
+-----------------------------------------+--------------------
+ {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID} | {HEAP_XMIN_FROZEN}
(1 row)
-- HEAP_MOVED = (HEAP_MOVED_IN | HEAP_MOVED_OFF)
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_MOVED}
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
+ raw_flags | combined_flags
+--------------------------------+----------------
+ {HEAP_MOVED_OFF,HEAP_MOVED_IN} | {HEAP_MOVED}
(1 row)
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, false);
- heap_tuple_infomask_flags
---------------------------------
- {HEAP_MOVED_IN,HEAP_MOVED_OFF}
-(1 row)
-
--- HEAP_LOCKED_UPGRADED = (HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY)
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_LOCKED_UPGRADED}
-(1 row)
-
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, false);
- heap_tuple_infomask_flags
-------------------------------------------
- {HEAP_XMAX_LOCK_ONLY,HEAP_XMAX_IS_MULTI}
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
+ raw_flags | combined_flags
+--------------------------------+----------------
+ {HEAP_MOVED_OFF,HEAP_MOVED_IN} | {HEAP_MOVED}
(1 row)
-- test all flags of t_infomask and t_infomask2
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, false))
- AS flags ORDER BY 1;
- flags
+SELECT unnest(raw_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
+ unnest
-----------------------
HEAP_COMBOCID
HEAP_HASEXTERNAL
@@ -182,85 +157,28 @@ SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, false))
HEAP_XMIN_INVALID
(19 rows)
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, true))
- AS flags ORDER BY 1;
- flags
----------------------
- HEAP_COMBOCID
- HEAP_HASEXTERNAL
- HEAP_HASNULL
- HEAP_HASOID_OLD
- HEAP_HASVARWIDTH
- HEAP_HOT_UPDATED
- HEAP_KEYS_UPDATED
+SELECT unnest(combined_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
+ unnest
+--------------------
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)
+(3 rows)
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, false))
- AS flags ORDER BY 1;
- flags
------------------------
- 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)
+-- no flags at all
+SELECT * FROM heap_tuple_infomask_flags(0, 0);
+ raw_flags | combined_flags
+-----------+----------------
+ {} | {}
+(1 row)
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, true))
- AS flags ORDER BY 1;
- flags
----------------------
- 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)
-
--- no flags
-SELECT unnest(heap_tuple_infomask_flags(0, 0, false));
- unnest
---------
-(0 rows)
-
-SELECT unnest(heap_tuple_infomask_flags(0, 0, true));
- unnest
---------
-(0 rows)
+-- no combined flags
+SELECT * FROM heap_tuple_infomask_flags(x'0010'::int, 0);
+ raw_flags | combined_flags
+-------------------------+----------------
+ {HEAP_XMAX_KEYSHR_LOCK} | {}
+(1 row)
DROP TABLE test1;
-- check that using any of these functions with a partitioned table or index
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 68f16cd400..4db4cd9f97 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -507,99 +507,118 @@ PG_FUNCTION_INFO_V1(heap_tuple_infomask_flags);
Datum
heap_tuple_infomask_flags(PG_FUNCTION_ARGS)
{
+#define HEAP_TUPLE_INFOMASK_COLS 2
+ Datum values[HEAP_TUPLE_INFOMASK_COLS];
+ bool nulls[HEAP_TUPLE_INFOMASK_COLS];
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;
+ Datum *flags;
+ TupleDesc tupdesc;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions")));
+ /* Build a tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
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));
+ /* Initialize values and NULL flags arrays */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, 0, sizeof(nulls));
- d = (Datum *) palloc0(sizeof(Datum) * bitcnt);
+ /* If no flags, return a set of empty arrays */
+ if (bitcnt <= 0)
+ {
+ values[0] = PointerGetDatum(construct_empty_array(TEXTOID));
+ values[1] = PointerGetDatum(construct_empty_array(TEXTOID));
+ PG_RETURN_DATUM(
+ HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+ }
+
+ /* build set of raw flags */
+ flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
/* decode t_infomask */
if ((t_infomask & HEAP_HASNULL) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASNULL");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASNULL");
if ((t_infomask & HEAP_HASVARWIDTH) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASVARWIDTH");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASVARWIDTH");
if ((t_infomask & HEAP_HASEXTERNAL) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASEXTERNAL");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASEXTERNAL");
if ((t_infomask & HEAP_HASOID_OLD) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASOID_OLD");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASOID_OLD");
+ if ((t_infomask & HEAP_XMAX_KEYSHR_LOCK) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_KEYSHR_LOCK");
if ((t_infomask & HEAP_COMBOCID) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_COMBOCID");
+ flags[cnt++] = CStringGetTextDatum("HEAP_COMBOCID");
+ if ((t_infomask & HEAP_XMAX_EXCL_LOCK) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_EXCL_LOCK");
+ if ((t_infomask & HEAP_XMAX_LOCK_ONLY) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_LOCK_ONLY");
+ if ((t_infomask & HEAP_XMIN_COMMITTED) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_COMMITTED");
+ if ((t_infomask & HEAP_XMIN_INVALID) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_INVALID");
if ((t_infomask & HEAP_XMAX_COMMITTED) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_XMAX_COMMITTED");
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_COMMITTED");
if ((t_infomask & HEAP_XMAX_INVALID) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_XMAX_INVALID");
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_INVALID");
+ if ((t_infomask & HEAP_XMAX_IS_MULTI) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_IS_MULTI");
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");
- }
+ flags[cnt++] = CStringGetTextDatum("HEAP_UPDATED");
+ if ((t_infomask & HEAP_MOVED_OFF) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_MOVED_OFF");
+ if ((t_infomask & HEAP_MOVED_IN) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_MOVED_IN");
/* decode t_infomask2 */
if ((t_infomask2 & HEAP_KEYS_UPDATED) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_KEYS_UPDATED");
+ flags[cnt++] = CStringGetTextDatum("HEAP_KEYS_UPDATED");
if ((t_infomask2 & HEAP_HOT_UPDATED) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HOT_UPDATED");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HOT_UPDATED");
if ((t_infomask2 & HEAP_ONLY_TUPLE) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_ONLY_TUPLE");
+ flags[cnt++] = CStringGetTextDatum("HEAP_ONLY_TUPLE");
+ /* build value */
Assert(cnt <= bitcnt);
- a = construct_array(d, cnt, TEXTOID, -1, false, 'i');
+ a = construct_array(flags, cnt, TEXTOID, -1, false, 'i');
+ pfree(flags);
+ values[0] = PointerGetDatum(a);
- pfree(d);
+ /*
+ * Build set of combined flags. Use the same size as previously
+ * for the allocation, this likely wastes a couple of bites but
+ * it keeps the code simple.
+ */
+ cnt = 0;
+ flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
- PG_RETURN_POINTER(a);
+ /* decode combined masks of t_infomaks */
+ if ((t_infomask & HEAP_XMAX_SHR_LOCK) == HEAP_XMAX_SHR_LOCK)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_SHR_LOCK");
+ if ((t_infomask & HEAP_XMIN_FROZEN) == HEAP_XMIN_FROZEN)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_FROZEN");
+ if ((t_infomask & HEAP_MOVED) == HEAP_MOVED)
+ flags[cnt++] = CStringGetTextDatum("HEAP_MOVED");
+
+ /* Build an empty array if there are no combined flags */
+ if (cnt == 0)
+ a = construct_empty_array(TEXTOID);
+ else
+ a = construct_array(flags, cnt, TEXTOID, -1, false, 'i');
+ pfree(flags);
+ values[1] = PointerGetDatum(a);
+
+ /* Returns the record as Datum */
+ PG_RETURN_DATUM(
+ HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
}
diff --git a/contrib/pageinspect/pageinspect--1.7--1.8.sql b/contrib/pageinspect/pageinspect--1.7--1.8.sql
index 7e85677d6c..459e43f9a2 100644
--- a/contrib/pageinspect/pageinspect--1.7--1.8.sql
+++ b/contrib/pageinspect/pageinspect--1.7--1.8.sql
@@ -9,7 +9,8 @@
CREATE FUNCTION heap_tuple_infomask_flags(
t_infomask integer,
t_infomask2 integer,
- decode_combined boolean DEFAULT false)
-RETURNS text[]
+ raw_flags OUT text[],
+ combined_flags OUT text[])
+RETURNS SETOF record
AS 'MODULE_PATHNAME', 'heap_tuple_infomask_flags'
LANGUAGE C STRICT PARALLEL SAFE;
diff --git a/contrib/pageinspect/sql/page.sql b/contrib/pageinspect/sql/page.sql
index 0319b5fa11..a78fd56b1c 100644
--- a/contrib/pageinspect/sql/page.sql
+++ b/contrib/pageinspect/sql/page.sql
@@ -36,42 +36,34 @@ SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0));
-- default: HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID.
VACUUM FREEZE test1;
-SELECT t_infomask, t_infomask2, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(flags);
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
-- output the decoded flag HEAP_XMIN_FROZEN instead
-SELECT t_infomask, t_infomask2, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2, true) m(flags);
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
-- tests for decoding of combined flags
-- HEAP_XMAX_SHR_LOCK = (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, false);
+SELECT * FROM heap_tuple_infomask_flags(x'0050'::int, 0);
-- HEAP_XMIN_FROZEN = (HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID)
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, false);
+SELECT * FROM heap_tuple_infomask_flags(x'0300'::int, 0);
-- HEAP_MOVED = (HEAP_MOVED_IN | HEAP_MOVED_OFF)
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, false);
--- HEAP_LOCKED_UPGRADED = (HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY)
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, false);
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
-- test all flags of t_infomask and t_infomask2
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, false))
- AS flags ORDER BY 1;
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, true))
- AS flags ORDER BY 1;
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, false))
- AS flags ORDER BY 1;
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, true))
- AS flags ORDER BY 1;
+SELECT unnest(raw_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
+SELECT unnest(combined_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
--- no flags
-SELECT unnest(heap_tuple_infomask_flags(0, 0, false));
-SELECT unnest(heap_tuple_infomask_flags(0, 0, true));
+-- no flags at all
+SELECT * FROM heap_tuple_infomask_flags(0, 0);
+-- no combined flags
+SELECT * FROM heap_tuple_infomask_flags(x'0010'::int, 0);
DROP TABLE test1;
diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml
index a7da3364a1..5e41d454fb 100644
--- a/doc/src/sgml/pageinspect.sgml
+++ b/doc/src/sgml/pageinspect.sgml
@@ -244,7 +244,7 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class
<varlistentry>
<term>
- <function>heap_tuple_infomask_flags(t_infomask integer, t_infomask2 integer, decode_combined bool) returns text[]</function>
+ <function>heap_tuple_infomask_flags(t_infomask integer, t_infomask2 integer) returns setof record</function>
<indexterm>
<primary>heap_tuple_infomask_flags</primary>
</indexterm>
@@ -255,21 +255,21 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class
<structfield>t_infomask</structfield> and
<structfield>t_infomask2</structfield> returned by
<function>heap_page_items</function> into a human-readable
- array of flag names. For example:
+ set of arrays make of flag names, with one column for all
+ the flags and one column for combined flags. For example:
<screen>
-test=# SELECT t_ctid, heap_tuple_infomask_flags(t_infomask, t_infomask2) AS flags
- FROM heap_page_items(get_raw_page('pg_class', 0))
+test=# SELECT t_ctid, raw_flags, combined_flags
+ FROM heap_page_items(get_raw_page('pg_class', 0)),
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2)
WHERE t_infomask IS NOT NULL OR t_infomask2 IS NOT NULL;
</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>,
- combined flags like <literal>HEAP_XMIN_FROZEN</literal> are
- returned instead of raw flags (<literal>HEAP_XMIN_COMMITTED</literal>
- and <literal>HEAP_XMIN_INVALID</literal> in this case). Default value
- is <literal>false</literal>.
+ Combined flags include for example <literal>HEAP_XMIN_FROZEN</literal>
+ which is defined as a set of <literal>HEAP_XMIN_COMMITTED</literal>
+ and <literal>HEAP_XMIN_INVALID</literal>.
</para>
<para>
See <filename>src/include/access/htup_details.h</filename> for
On 2019-Sep-16, Michael Paquier wrote:
On Mon, Sep 16, 2019 at 11:46:16AM +0530, Amit Kapila wrote:
I don't see much use of separating information for infomask and infomask2.
Okay, using two separate columns leads to the attached. Any thoughts?
This also includes a fix for cases with IS_LOCKED_ONLY and UPGRADED.
I like how it looks in the expected test output. Didn't review the code
closely, but it looks reasonable in a quick glance.
Whitespace nitpick: pgindent will do something very annoying with this:
+ PG_RETURN_DATUM( + HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
I suggest to split the line in the first comma, not in the parens.
+ Combined flags include for example <literal>HEAP_XMIN_FROZEN</literal> + which is defined as a set of <literal>HEAP_XMIN_COMMITTED</literal> + and <literal>HEAP_XMIN_INVALID</literal>.
I suggest something like "Combined flags are displayed for source-level
macros that take into account the value of more than one raw bit, such
as HEAP_XMIN_FROZEN". (We probably don't want an exhaustive list, which
becomes annoying to maintain; users can refer to the source file.)
There's a typo "bites" in a comment.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Mon, Sep 16, 2019 at 11:11:06AM -0300, Alvaro Herrera wrote:
On 2019-Sep-16, Michael Paquier wrote:
On Mon, Sep 16, 2019 at 11:46:16AM +0530, Amit Kapila wrote:
Okay, using two separate columns leads to the attached. Any thoughts?
This also includes a fix for cases with IS_LOCKED_ONLY and UPGRADED.I like how it looks in the expected test output. Didn't review the code
closely, but it looks reasonable in a quick glance.
Thanks for the review.
Whitespace nitpick: pgindent will do something very annoying with
this:+ PG_RETURN_DATUM( + HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));I suggest to split the line in the first comma, not in the parens.
Indeed. I am switching to use a HeapTuple as intermediate step
instead.
+ Combined flags include for example <literal>HEAP_XMIN_FROZEN</literal> + which is defined as a set of <literal>HEAP_XMIN_COMMITTED</literal> + and <literal>HEAP_XMIN_INVALID</literal>.I suggest something like "Combined flags are displayed for source-level
macros that take into account the value of more than one raw bit, such
as HEAP_XMIN_FROZEN". (We probably don't want an exhaustive list, which
becomes annoying to maintain; users can refer to the source file.)
Yes, I didn't want to provide a list for that exact reason, and your
suggestion of change sounds fine to me.
There's a typo "bites" in a comment.
Thanks, fixed.
Amit, what do you think? Does the patch match with what you have in
mind?
--
Michael
Attachments:
pageinspect-combine-flags-3.patchtext/x-diff; charset=us-asciiDownload
diff --git a/contrib/pageinspect/expected/page.out b/contrib/pageinspect/expected/page.out
index 6a09d46a57..c8f6e1b4c6 100644
--- a/contrib/pageinspect/expected/page.out
+++ b/contrib/pageinspect/expected/page.out
@@ -86,80 +86,55 @@ SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0));
-- 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, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(flags);
- t_infomask | t_infomask2 | flags
-------------+-------------+-----------------------------------------------------------
- 2816 | 2 | {HEAP_XMAX_INVALID,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID}
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
+ t_infomask | t_infomask2 | raw_flags | combined_flags
+------------+-------------+-----------------------------------------------------------+--------------------
+ 2816 | 2 | {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID} | {HEAP_XMIN_FROZEN}
(1 row)
-- output the decoded flag HEAP_XMIN_FROZEN instead
-SELECT t_infomask, t_infomask2, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2, true) m(flags);
- t_infomask | t_infomask2 | flags
-------------+-------------+--------------------------------------
- 2816 | 2 | {HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN}
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
+ t_infomask | t_infomask2 | raw_flags | combined_flags
+------------+-------------+-----------------------------------------------------------+--------------------
+ 2816 | 2 | {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID} | {HEAP_XMIN_FROZEN}
(1 row)
-- tests for decoding of combined flags
-- HEAP_XMAX_SHR_LOCK = (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_XMAX_SHR_LOCK}
-(1 row)
-
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, false);
- heap_tuple_infomask_flags
----------------------------------------------
- {HEAP_XMAX_EXCL_LOCK,HEAP_XMAX_KEYSHR_LOCK}
+SELECT * FROM heap_tuple_infomask_flags(x'0050'::int, 0);
+ raw_flags | combined_flags
+---------------------------------------------+----------------------
+ {HEAP_XMAX_KEYSHR_LOCK,HEAP_XMAX_EXCL_LOCK} | {HEAP_XMAX_SHR_LOCK}
(1 row)
-- HEAP_XMIN_FROZEN = (HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID)
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_XMIN_FROZEN}
-(1 row)
-
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, false);
- heap_tuple_infomask_flags
------------------------------------------
- {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID}
+SELECT * FROM heap_tuple_infomask_flags(x'0300'::int, 0);
+ raw_flags | combined_flags
+-----------------------------------------+--------------------
+ {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID} | {HEAP_XMIN_FROZEN}
(1 row)
-- HEAP_MOVED = (HEAP_MOVED_IN | HEAP_MOVED_OFF)
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_MOVED}
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
+ raw_flags | combined_flags
+--------------------------------+----------------
+ {HEAP_MOVED_OFF,HEAP_MOVED_IN} | {HEAP_MOVED}
(1 row)
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, false);
- heap_tuple_infomask_flags
---------------------------------
- {HEAP_MOVED_IN,HEAP_MOVED_OFF}
-(1 row)
-
--- HEAP_LOCKED_UPGRADED = (HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY)
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_LOCKED_UPGRADED}
-(1 row)
-
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, false);
- heap_tuple_infomask_flags
-------------------------------------------
- {HEAP_XMAX_LOCK_ONLY,HEAP_XMAX_IS_MULTI}
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
+ raw_flags | combined_flags
+--------------------------------+----------------
+ {HEAP_MOVED_OFF,HEAP_MOVED_IN} | {HEAP_MOVED}
(1 row)
-- test all flags of t_infomask and t_infomask2
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, false))
- AS flags ORDER BY 1;
- flags
+SELECT unnest(raw_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
+ unnest
-----------------------
HEAP_COMBOCID
HEAP_HASEXTERNAL
@@ -182,85 +157,28 @@ SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, false))
HEAP_XMIN_INVALID
(19 rows)
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, true))
- AS flags ORDER BY 1;
- flags
----------------------
- HEAP_COMBOCID
- HEAP_HASEXTERNAL
- HEAP_HASNULL
- HEAP_HASOID_OLD
- HEAP_HASVARWIDTH
- HEAP_HOT_UPDATED
- HEAP_KEYS_UPDATED
+SELECT unnest(combined_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
+ unnest
+--------------------
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)
+(3 rows)
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, false))
- AS flags ORDER BY 1;
- flags
------------------------
- 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)
+-- no flags at all
+SELECT * FROM heap_tuple_infomask_flags(0, 0);
+ raw_flags | combined_flags
+-----------+----------------
+ {} | {}
+(1 row)
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, true))
- AS flags ORDER BY 1;
- flags
----------------------
- 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)
-
--- no flags
-SELECT unnest(heap_tuple_infomask_flags(0, 0, false));
- unnest
---------
-(0 rows)
-
-SELECT unnest(heap_tuple_infomask_flags(0, 0, true));
- unnest
---------
-(0 rows)
+-- no combined flags
+SELECT * FROM heap_tuple_infomask_flags(x'0010'::int, 0);
+ raw_flags | combined_flags
+-------------------------+----------------
+ {HEAP_XMAX_KEYSHR_LOCK} | {}
+(1 row)
DROP TABLE test1;
-- check that using any of these functions with a partitioned table or index
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 68f16cd400..7d988f5c04 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -507,99 +507,119 @@ PG_FUNCTION_INFO_V1(heap_tuple_infomask_flags);
Datum
heap_tuple_infomask_flags(PG_FUNCTION_ARGS)
{
+#define HEAP_TUPLE_INFOMASK_COLS 2
+ Datum values[HEAP_TUPLE_INFOMASK_COLS];
+ bool nulls[HEAP_TUPLE_INFOMASK_COLS];
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;
+ Datum *flags;
+ TupleDesc tupdesc;
+ HeapTuple tuple;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions")));
+ /* Build a tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
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));
+ /* Initialize values and NULL flags arrays */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, 0, sizeof(nulls));
- d = (Datum *) palloc0(sizeof(Datum) * bitcnt);
+ /* If no flags, return a set of empty arrays */
+ if (bitcnt <= 0)
+ {
+ values[0] = PointerGetDatum(construct_empty_array(TEXTOID));
+ values[1] = PointerGetDatum(construct_empty_array(TEXTOID));
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+ PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+ }
+
+ /* build set of raw flags */
+ flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
/* decode t_infomask */
if ((t_infomask & HEAP_HASNULL) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASNULL");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASNULL");
if ((t_infomask & HEAP_HASVARWIDTH) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASVARWIDTH");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASVARWIDTH");
if ((t_infomask & HEAP_HASEXTERNAL) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASEXTERNAL");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASEXTERNAL");
if ((t_infomask & HEAP_HASOID_OLD) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASOID_OLD");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASOID_OLD");
+ if ((t_infomask & HEAP_XMAX_KEYSHR_LOCK) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_KEYSHR_LOCK");
if ((t_infomask & HEAP_COMBOCID) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_COMBOCID");
+ flags[cnt++] = CStringGetTextDatum("HEAP_COMBOCID");
+ if ((t_infomask & HEAP_XMAX_EXCL_LOCK) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_EXCL_LOCK");
+ if ((t_infomask & HEAP_XMAX_LOCK_ONLY) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_LOCK_ONLY");
+ if ((t_infomask & HEAP_XMIN_COMMITTED) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_COMMITTED");
+ if ((t_infomask & HEAP_XMIN_INVALID) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_INVALID");
if ((t_infomask & HEAP_XMAX_COMMITTED) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_XMAX_COMMITTED");
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_COMMITTED");
if ((t_infomask & HEAP_XMAX_INVALID) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_XMAX_INVALID");
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_INVALID");
+ if ((t_infomask & HEAP_XMAX_IS_MULTI) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_IS_MULTI");
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");
- }
+ flags[cnt++] = CStringGetTextDatum("HEAP_UPDATED");
+ if ((t_infomask & HEAP_MOVED_OFF) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_MOVED_OFF");
+ if ((t_infomask & HEAP_MOVED_IN) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_MOVED_IN");
/* decode t_infomask2 */
if ((t_infomask2 & HEAP_KEYS_UPDATED) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_KEYS_UPDATED");
+ flags[cnt++] = CStringGetTextDatum("HEAP_KEYS_UPDATED");
if ((t_infomask2 & HEAP_HOT_UPDATED) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HOT_UPDATED");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HOT_UPDATED");
if ((t_infomask2 & HEAP_ONLY_TUPLE) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_ONLY_TUPLE");
+ flags[cnt++] = CStringGetTextDatum("HEAP_ONLY_TUPLE");
+ /* build value */
Assert(cnt <= bitcnt);
- a = construct_array(d, cnt, TEXTOID, -1, false, 'i');
+ a = construct_array(flags, cnt, TEXTOID, -1, false, 'i');
+ pfree(flags);
+ values[0] = PointerGetDatum(a);
- pfree(d);
+ /*
+ * Build set of combined flags. Use the same size as previously for the
+ * allocation, this likely wastes a couple of bytes but it keeps the code
+ * simple.
+ */
+ cnt = 0;
+ flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
- PG_RETURN_POINTER(a);
+ /* decode combined masks of t_infomask */
+ if ((t_infomask & HEAP_XMAX_SHR_LOCK) == HEAP_XMAX_SHR_LOCK)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_SHR_LOCK");
+ if ((t_infomask & HEAP_XMIN_FROZEN) == HEAP_XMIN_FROZEN)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_FROZEN");
+ if ((t_infomask & HEAP_MOVED) == HEAP_MOVED)
+ flags[cnt++] = CStringGetTextDatum("HEAP_MOVED");
+
+ /* Build an empty array if there are no combined flags */
+ if (cnt == 0)
+ a = construct_empty_array(TEXTOID);
+ else
+ a = construct_array(flags, cnt, TEXTOID, -1, false, 'i');
+ pfree(flags);
+ values[1] = PointerGetDatum(a);
+
+ /* Returns the record as Datum */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+ PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
}
diff --git a/contrib/pageinspect/pageinspect--1.7--1.8.sql b/contrib/pageinspect/pageinspect--1.7--1.8.sql
index 7e85677d6c..459e43f9a2 100644
--- a/contrib/pageinspect/pageinspect--1.7--1.8.sql
+++ b/contrib/pageinspect/pageinspect--1.7--1.8.sql
@@ -9,7 +9,8 @@
CREATE FUNCTION heap_tuple_infomask_flags(
t_infomask integer,
t_infomask2 integer,
- decode_combined boolean DEFAULT false)
-RETURNS text[]
+ raw_flags OUT text[],
+ combined_flags OUT text[])
+RETURNS SETOF record
AS 'MODULE_PATHNAME', 'heap_tuple_infomask_flags'
LANGUAGE C STRICT PARALLEL SAFE;
diff --git a/contrib/pageinspect/sql/page.sql b/contrib/pageinspect/sql/page.sql
index 0319b5fa11..a78fd56b1c 100644
--- a/contrib/pageinspect/sql/page.sql
+++ b/contrib/pageinspect/sql/page.sql
@@ -36,42 +36,34 @@ SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0));
-- default: HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID.
VACUUM FREEZE test1;
-SELECT t_infomask, t_infomask2, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(flags);
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
-- output the decoded flag HEAP_XMIN_FROZEN instead
-SELECT t_infomask, t_infomask2, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2, true) m(flags);
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
-- tests for decoding of combined flags
-- HEAP_XMAX_SHR_LOCK = (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, false);
+SELECT * FROM heap_tuple_infomask_flags(x'0050'::int, 0);
-- HEAP_XMIN_FROZEN = (HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID)
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, false);
+SELECT * FROM heap_tuple_infomask_flags(x'0300'::int, 0);
-- HEAP_MOVED = (HEAP_MOVED_IN | HEAP_MOVED_OFF)
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, false);
--- HEAP_LOCKED_UPGRADED = (HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY)
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, false);
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
-- test all flags of t_infomask and t_infomask2
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, false))
- AS flags ORDER BY 1;
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, true))
- AS flags ORDER BY 1;
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, false))
- AS flags ORDER BY 1;
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, true))
- AS flags ORDER BY 1;
+SELECT unnest(raw_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
+SELECT unnest(combined_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
--- no flags
-SELECT unnest(heap_tuple_infomask_flags(0, 0, false));
-SELECT unnest(heap_tuple_infomask_flags(0, 0, true));
+-- no flags at all
+SELECT * FROM heap_tuple_infomask_flags(0, 0);
+-- no combined flags
+SELECT * FROM heap_tuple_infomask_flags(x'0010'::int, 0);
DROP TABLE test1;
diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml
index a7da3364a1..42f1f0af78 100644
--- a/doc/src/sgml/pageinspect.sgml
+++ b/doc/src/sgml/pageinspect.sgml
@@ -244,7 +244,7 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class
<varlistentry>
<term>
- <function>heap_tuple_infomask_flags(t_infomask integer, t_infomask2 integer, decode_combined bool) returns text[]</function>
+ <function>heap_tuple_infomask_flags(t_infomask integer, t_infomask2 integer) returns setof record</function>
<indexterm>
<primary>heap_tuple_infomask_flags</primary>
</indexterm>
@@ -255,21 +255,21 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class
<structfield>t_infomask</structfield> and
<structfield>t_infomask2</structfield> returned by
<function>heap_page_items</function> into a human-readable
- array of flag names. For example:
+ set of arrays make of flag names, with one column for all
+ the flags and one column for combined flags. For example:
<screen>
-test=# SELECT t_ctid, heap_tuple_infomask_flags(t_infomask, t_infomask2) AS flags
- FROM heap_page_items(get_raw_page('pg_class', 0))
+test=# SELECT t_ctid, raw_flags, combined_flags
+ FROM heap_page_items(get_raw_page('pg_class', 0)),
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2)
WHERE t_infomask IS NOT NULL OR t_infomask2 IS NOT NULL;
</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>,
- combined flags like <literal>HEAP_XMIN_FROZEN</literal> are
- returned instead of raw flags (<literal>HEAP_XMIN_COMMITTED</literal>
- and <literal>HEAP_XMIN_INVALID</literal> in this case). Default value
- is <literal>false</literal>.
+ Combined flags are displayed for source-level macros that take into
+ account the value of more than one raw bit, such as
+ <literal>HEAP_XMIN_FROZEN</litetal>.
</para>
<para>
See <filename>src/include/access/htup_details.h</filename> for
On Tue, Sep 17, 2019 at 6:14 AM Michael Paquier <michael@paquier.xyz> wrote:
On Mon, Sep 16, 2019 at 11:11:06AM -0300, Alvaro Herrera wrote:
On 2019-Sep-16, Michael Paquier wrote:
Thanks, fixed.
Amit, what do you think? Does the patch match with what you have in
mind?
*
CREATE FUNCTION heap_tuple_infomask_flags(
t_infomask integer,
t_infomask2 integer,
- decode_combined boolean DEFAULT false)
-RETURNS text[]
+ raw_flags OUT text[],
+ combined_flags OUT text[])
+RETURNS SETOF record
We always return a single tuple/record from this function, so do we
really need to return SETOF record or just returning record is
sufficient?
*
+ pfree(flags);
+ values[0] = PointerGetDatum(a);
- pfree(d);
+ /*
+ * Build set of combined flags. Use the same size as previously for the
+ * allocation, this likely wastes a couple of bytes but it keeps the code
+ * simple.
+ */
+ cnt = 0;
+ flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
If you want to use the same size array, then you might want to just
memset the previous array rather than first freeing it and then again
allocating it. This is not a big point, so any which way is fine.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Tue, Sep 17, 2019 at 09:23:45AM +0530, Amit Kapila wrote:
We always return a single tuple/record from this function, so do we
really need to return SETOF record or just returning record is
sufficient?
Right (with the doc update).
If you want to use the same size array, then you might want to just
memset the previous array rather than first freeing it and then again
allocating it. This is not a big point, so any which way is fine.
Sure. This is less expensive though, so changed it the way you
are suggesting on my local branch.
--
Michael
On Tue, Sep 17, 2019 at 01:06:18PM +0900, Michael Paquier wrote:
On Tue, Sep 17, 2019 at 09:23:45AM +0530, Amit Kapila wrote:
If you want to use the same size array, then you might want to just
memset the previous array rather than first freeing it and then again
allocating it. This is not a big point, so any which way is fine.Sure. This is less expensive though, so changed it the way you
are suggesting on my local branch.
I am attaching an updated patch for now that I would like to commit.
Are there more comments about the shape of the patch, the name of the
columns for the function, etc.?
--
Michael
Attachments:
pageinspect-combine-flags-4.patchtext/x-diff; charset=us-asciiDownload
diff --git a/contrib/pageinspect/expected/page.out b/contrib/pageinspect/expected/page.out
index 6a09d46a57..c8f6e1b4c6 100644
--- a/contrib/pageinspect/expected/page.out
+++ b/contrib/pageinspect/expected/page.out
@@ -86,80 +86,55 @@ SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0));
-- 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, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(flags);
- t_infomask | t_infomask2 | flags
-------------+-------------+-----------------------------------------------------------
- 2816 | 2 | {HEAP_XMAX_INVALID,HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID}
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
+ t_infomask | t_infomask2 | raw_flags | combined_flags
+------------+-------------+-----------------------------------------------------------+--------------------
+ 2816 | 2 | {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID} | {HEAP_XMIN_FROZEN}
(1 row)
-- output the decoded flag HEAP_XMIN_FROZEN instead
-SELECT t_infomask, t_infomask2, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2, true) m(flags);
- t_infomask | t_infomask2 | flags
-------------+-------------+--------------------------------------
- 2816 | 2 | {HEAP_XMAX_INVALID,HEAP_XMIN_FROZEN}
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
+ t_infomask | t_infomask2 | raw_flags | combined_flags
+------------+-------------+-----------------------------------------------------------+--------------------
+ 2816 | 2 | {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID} | {HEAP_XMIN_FROZEN}
(1 row)
-- tests for decoding of combined flags
-- HEAP_XMAX_SHR_LOCK = (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_XMAX_SHR_LOCK}
-(1 row)
-
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, false);
- heap_tuple_infomask_flags
----------------------------------------------
- {HEAP_XMAX_EXCL_LOCK,HEAP_XMAX_KEYSHR_LOCK}
+SELECT * FROM heap_tuple_infomask_flags(x'0050'::int, 0);
+ raw_flags | combined_flags
+---------------------------------------------+----------------------
+ {HEAP_XMAX_KEYSHR_LOCK,HEAP_XMAX_EXCL_LOCK} | {HEAP_XMAX_SHR_LOCK}
(1 row)
-- HEAP_XMIN_FROZEN = (HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID)
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_XMIN_FROZEN}
-(1 row)
-
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, false);
- heap_tuple_infomask_flags
------------------------------------------
- {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID}
+SELECT * FROM heap_tuple_infomask_flags(x'0300'::int, 0);
+ raw_flags | combined_flags
+-----------------------------------------+--------------------
+ {HEAP_XMIN_COMMITTED,HEAP_XMIN_INVALID} | {HEAP_XMIN_FROZEN}
(1 row)
-- HEAP_MOVED = (HEAP_MOVED_IN | HEAP_MOVED_OFF)
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_MOVED}
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
+ raw_flags | combined_flags
+--------------------------------+----------------
+ {HEAP_MOVED_OFF,HEAP_MOVED_IN} | {HEAP_MOVED}
(1 row)
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, false);
- heap_tuple_infomask_flags
---------------------------------
- {HEAP_MOVED_IN,HEAP_MOVED_OFF}
-(1 row)
-
--- HEAP_LOCKED_UPGRADED = (HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY)
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, true);
- heap_tuple_infomask_flags
----------------------------
- {HEAP_LOCKED_UPGRADED}
-(1 row)
-
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, false);
- heap_tuple_infomask_flags
-------------------------------------------
- {HEAP_XMAX_LOCK_ONLY,HEAP_XMAX_IS_MULTI}
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
+ raw_flags | combined_flags
+--------------------------------+----------------
+ {HEAP_MOVED_OFF,HEAP_MOVED_IN} | {HEAP_MOVED}
(1 row)
-- test all flags of t_infomask and t_infomask2
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, false))
- AS flags ORDER BY 1;
- flags
+SELECT unnest(raw_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
+ unnest
-----------------------
HEAP_COMBOCID
HEAP_HASEXTERNAL
@@ -182,85 +157,28 @@ SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, false))
HEAP_XMIN_INVALID
(19 rows)
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, true))
- AS flags ORDER BY 1;
- flags
----------------------
- HEAP_COMBOCID
- HEAP_HASEXTERNAL
- HEAP_HASNULL
- HEAP_HASOID_OLD
- HEAP_HASVARWIDTH
- HEAP_HOT_UPDATED
- HEAP_KEYS_UPDATED
+SELECT unnest(combined_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
+ unnest
+--------------------
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)
+(3 rows)
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, false))
- AS flags ORDER BY 1;
- flags
------------------------
- 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)
+-- no flags at all
+SELECT * FROM heap_tuple_infomask_flags(0, 0);
+ raw_flags | combined_flags
+-----------+----------------
+ {} | {}
+(1 row)
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, true))
- AS flags ORDER BY 1;
- flags
----------------------
- 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)
-
--- no flags
-SELECT unnest(heap_tuple_infomask_flags(0, 0, false));
- unnest
---------
-(0 rows)
-
-SELECT unnest(heap_tuple_infomask_flags(0, 0, true));
- unnest
---------
-(0 rows)
+-- no combined flags
+SELECT * FROM heap_tuple_infomask_flags(x'0010'::int, 0);
+ raw_flags | combined_flags
+-------------------------+----------------
+ {HEAP_XMAX_KEYSHR_LOCK} | {}
+(1 row)
DROP TABLE test1;
-- check that using any of these functions with a partitioned table or index
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 68f16cd400..2cad296e22 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -507,99 +507,117 @@ PG_FUNCTION_INFO_V1(heap_tuple_infomask_flags);
Datum
heap_tuple_infomask_flags(PG_FUNCTION_ARGS)
{
+#define HEAP_TUPLE_INFOMASK_COLS 2
+ Datum values[HEAP_TUPLE_INFOMASK_COLS];
+ bool nulls[HEAP_TUPLE_INFOMASK_COLS];
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;
+ Datum *flags;
+ TupleDesc tupdesc;
+ HeapTuple tuple;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions")));
+ /* Build a tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
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));
+ /* Initialize values and NULL flags arrays */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, 0, sizeof(nulls));
- d = (Datum *) palloc0(sizeof(Datum) * bitcnt);
+ /* If no flags, return a set of empty arrays */
+ if (bitcnt <= 0)
+ {
+ values[0] = PointerGetDatum(construct_empty_array(TEXTOID));
+ values[1] = PointerGetDatum(construct_empty_array(TEXTOID));
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+ PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+ }
+
+ /* build set of raw flags */
+ flags = (Datum *) palloc0(sizeof(Datum) * bitcnt);
/* decode t_infomask */
if ((t_infomask & HEAP_HASNULL) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASNULL");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASNULL");
if ((t_infomask & HEAP_HASVARWIDTH) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASVARWIDTH");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASVARWIDTH");
if ((t_infomask & HEAP_HASEXTERNAL) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASEXTERNAL");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASEXTERNAL");
if ((t_infomask & HEAP_HASOID_OLD) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HASOID_OLD");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HASOID_OLD");
+ if ((t_infomask & HEAP_XMAX_KEYSHR_LOCK) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_KEYSHR_LOCK");
if ((t_infomask & HEAP_COMBOCID) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_COMBOCID");
+ flags[cnt++] = CStringGetTextDatum("HEAP_COMBOCID");
+ if ((t_infomask & HEAP_XMAX_EXCL_LOCK) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_EXCL_LOCK");
+ if ((t_infomask & HEAP_XMAX_LOCK_ONLY) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_LOCK_ONLY");
+ if ((t_infomask & HEAP_XMIN_COMMITTED) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_COMMITTED");
+ if ((t_infomask & HEAP_XMIN_INVALID) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_INVALID");
if ((t_infomask & HEAP_XMAX_COMMITTED) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_XMAX_COMMITTED");
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_COMMITTED");
if ((t_infomask & HEAP_XMAX_INVALID) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_XMAX_INVALID");
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_INVALID");
+ if ((t_infomask & HEAP_XMAX_IS_MULTI) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_IS_MULTI");
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");
- }
+ flags[cnt++] = CStringGetTextDatum("HEAP_UPDATED");
+ if ((t_infomask & HEAP_MOVED_OFF) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_MOVED_OFF");
+ if ((t_infomask & HEAP_MOVED_IN) != 0)
+ flags[cnt++] = CStringGetTextDatum("HEAP_MOVED_IN");
/* decode t_infomask2 */
if ((t_infomask2 & HEAP_KEYS_UPDATED) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_KEYS_UPDATED");
+ flags[cnt++] = CStringGetTextDatum("HEAP_KEYS_UPDATED");
if ((t_infomask2 & HEAP_HOT_UPDATED) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_HOT_UPDATED");
+ flags[cnt++] = CStringGetTextDatum("HEAP_HOT_UPDATED");
if ((t_infomask2 & HEAP_ONLY_TUPLE) != 0)
- d[cnt++] = CStringGetTextDatum("HEAP_ONLY_TUPLE");
+ flags[cnt++] = CStringGetTextDatum("HEAP_ONLY_TUPLE");
+ /* build value */
Assert(cnt <= bitcnt);
- a = construct_array(d, cnt, TEXTOID, -1, false, 'i');
+ a = construct_array(flags, cnt, TEXTOID, -1, false, 'i');
+ values[0] = PointerGetDatum(a);
- pfree(d);
+ /*
+ * Build set of combined flags. Use the same array as previously,
+ * this keeps the code simple.
+ */
+ cnt = 0;
+ MemSet(flags, 0, sizeof(Datum) * bitcnt);
- PG_RETURN_POINTER(a);
+ /* decode combined masks of t_infomask */
+ if ((t_infomask & HEAP_XMAX_SHR_LOCK) == HEAP_XMAX_SHR_LOCK)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMAX_SHR_LOCK");
+ if ((t_infomask & HEAP_XMIN_FROZEN) == HEAP_XMIN_FROZEN)
+ flags[cnt++] = CStringGetTextDatum("HEAP_XMIN_FROZEN");
+ if ((t_infomask & HEAP_MOVED) == HEAP_MOVED)
+ flags[cnt++] = CStringGetTextDatum("HEAP_MOVED");
+
+ /* Build an empty array if there are no combined flags */
+ if (cnt == 0)
+ a = construct_empty_array(TEXTOID);
+ else
+ a = construct_array(flags, cnt, TEXTOID, -1, false, 'i');
+ pfree(flags);
+ values[1] = PointerGetDatum(a);
+
+ /* Returns the record as Datum */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+ PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
}
diff --git a/contrib/pageinspect/pageinspect--1.7--1.8.sql b/contrib/pageinspect/pageinspect--1.7--1.8.sql
index 7e85677d6c..2a7c4b3516 100644
--- a/contrib/pageinspect/pageinspect--1.7--1.8.sql
+++ b/contrib/pageinspect/pageinspect--1.7--1.8.sql
@@ -9,7 +9,8 @@
CREATE FUNCTION heap_tuple_infomask_flags(
t_infomask integer,
t_infomask2 integer,
- decode_combined boolean DEFAULT false)
-RETURNS text[]
+ raw_flags OUT text[],
+ combined_flags OUT text[])
+RETURNS record
AS 'MODULE_PATHNAME', 'heap_tuple_infomask_flags'
LANGUAGE C STRICT PARALLEL SAFE;
diff --git a/contrib/pageinspect/sql/page.sql b/contrib/pageinspect/sql/page.sql
index 0319b5fa11..a78fd56b1c 100644
--- a/contrib/pageinspect/sql/page.sql
+++ b/contrib/pageinspect/sql/page.sql
@@ -36,42 +36,34 @@ SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0));
-- default: HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID.
VACUUM FREEZE test1;
-SELECT t_infomask, t_infomask2, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(flags);
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
-- output the decoded flag HEAP_XMIN_FROZEN instead
-SELECT t_infomask, t_infomask2, flags
+SELECT t_infomask, t_infomask2, raw_flags, combined_flags
FROM heap_page_items(get_raw_page('test1', 0)),
- LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2, true) m(flags);
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(raw_flags, combined_flags);
-- tests for decoding of combined flags
-- HEAP_XMAX_SHR_LOCK = (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'0050'::int, 0, false);
+SELECT * FROM heap_tuple_infomask_flags(x'0050'::int, 0);
-- HEAP_XMIN_FROZEN = (HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID)
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'0300'::int, 0, false);
+SELECT * FROM heap_tuple_infomask_flags(x'0300'::int, 0);
-- HEAP_MOVED = (HEAP_MOVED_IN | HEAP_MOVED_OFF)
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'C000'::int, 0, false);
--- HEAP_LOCKED_UPGRADED = (HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY)
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, true);
-SELECT heap_tuple_infomask_flags(x'1080'::int, 0, false);
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
+SELECT * FROM heap_tuple_infomask_flags(x'C000'::int, 0);
-- test all flags of t_infomask and t_infomask2
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, false))
- AS flags ORDER BY 1;
-SELECT unnest(heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int, true))
- AS flags ORDER BY 1;
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, false))
- AS flags ORDER BY 1;
-SELECT unnest(heap_tuple_infomask_flags(-1, -1, true))
- AS flags ORDER BY 1;
+SELECT unnest(raw_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
+SELECT unnest(combined_flags)
+ FROM heap_tuple_infomask_flags(x'FFFF'::int, x'FFFF'::int) ORDER BY 1;
--- no flags
-SELECT unnest(heap_tuple_infomask_flags(0, 0, false));
-SELECT unnest(heap_tuple_infomask_flags(0, 0, true));
+-- no flags at all
+SELECT * FROM heap_tuple_infomask_flags(0, 0);
+-- no combined flags
+SELECT * FROM heap_tuple_infomask_flags(x'0010'::int, 0);
DROP TABLE test1;
diff --git a/doc/src/sgml/pageinspect.sgml b/doc/src/sgml/pageinspect.sgml
index a7da3364a1..e009682ed6 100644
--- a/doc/src/sgml/pageinspect.sgml
+++ b/doc/src/sgml/pageinspect.sgml
@@ -244,7 +244,7 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class
<varlistentry>
<term>
- <function>heap_tuple_infomask_flags(t_infomask integer, t_infomask2 integer, decode_combined bool) returns text[]</function>
+ <function>heap_tuple_infomask_flags(t_infomask integer, t_infomask2 integer) returns record</function>
<indexterm>
<primary>heap_tuple_infomask_flags</primary>
</indexterm>
@@ -255,21 +255,21 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class
<structfield>t_infomask</structfield> and
<structfield>t_infomask2</structfield> returned by
<function>heap_page_items</function> into a human-readable
- array of flag names. For example:
+ set of arrays make of flag names, with one column for all
+ the flags and one column for combined flags. For example:
<screen>
-test=# SELECT t_ctid, heap_tuple_infomask_flags(t_infomask, t_infomask2) AS flags
- FROM heap_page_items(get_raw_page('pg_class', 0))
+test=# SELECT t_ctid, raw_flags, combined_flags
+ FROM heap_page_items(get_raw_page('pg_class', 0)),
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2)
WHERE t_infomask IS NOT NULL OR t_infomask2 IS NOT NULL;
</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>,
- combined flags like <literal>HEAP_XMIN_FROZEN</literal> are
- returned instead of raw flags (<literal>HEAP_XMIN_COMMITTED</literal>
- and <literal>HEAP_XMIN_INVALID</literal> in this case). Default value
- is <literal>false</literal>.
+ Combined flags are displayed for source-level macros that take into
+ account the value of more than one raw bit, such as
+ <literal>HEAP_XMIN_FROZEN</litetal>.
</para>
<para>
See <filename>src/include/access/htup_details.h</filename> for
On Wed, Sep 18, 2019 at 10:37:27AM +0900, Michael Paquier wrote:
I am attaching an updated patch for now that I would like to commit.
Are there more comments about the shape of the patch, the name of the
columns for the function, etc.?
Okay, I have done an extra round of review, and committed it.
--
Michael
On Thu, Sep 19, 2019 at 7:34 AM Michael Paquier <michael@paquier.xyz> wrote:
On Wed, Sep 18, 2019 at 10:37:27AM +0900, Michael Paquier wrote:
I am attaching an updated patch for now that I would like to commit.
Are there more comments about the shape of the patch, the name of the
columns for the function, etc.?Okay, I have done an extra round of review, and committed it.
Thanks. I was about to have a look today, but anyway I checked the
committed patch and it looks fine.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
On Thu, Sep 19, 2019 at 08:22:04AM +0530, Amit Kapila wrote:
Thanks. I was about to have a look today, but anyway I checked the
committed patch and it looks fine.
Thanks Amit for double-checking.
--
Michael
On Thu, 19 Sep 2019 at 10:04, Michael Paquier <michael@paquier.xyz> wrote:
On Wed, Sep 18, 2019 at 10:37:27AM +0900, Michael Paquier wrote:
I am attaching an updated patch for now that I would like to commit.
Are there more comments about the shape of the patch, the name of the
columns for the function, etc.?Okay, I have done an extra round of review, and committed it.
Thanks very much. That'll make life easier when debugging corruptions.
--
Craig Ringer http://www.2ndQuadrant.com/
2ndQuadrant - PostgreSQL Solutions for the Enterprise