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 \
(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, 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}
+(1 row)
+
+-- output the decoded flag HEAP_XMIN_FROZEN instead
+SELECT t_infomask, t_infomask2, 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}
+(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}
+(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}
+(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}
+(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}
+(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
+-----------------------
+ 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'::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
+ 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)
+
+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)
+
+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)
+
DROP TABLE test1;
-- check that using any of these functions with a partitioned table or index
-- would fail
#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"
PG_RETURN_ARRAYTYPE_P(res);
}
+
+/*
+ * heap_tuple_infomask_flags
+ *
+ * Decode into a human-readable format t_infomask and t_infomask2 associated
+ * to a tuple. All the flags are described in 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");
+
+ Assert(cnt <= bitcnt);
+ a = construct_array(d, cnt, TEXTOID, -1, false, 'i');
+
+ pfree(d);
+
+ PG_RETURN_POINTER(a);
+}
--- /dev/null
+/* 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;
# 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
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, flags
+FROM heap_page_items(get_raw_page('test1', 0)),
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2) m(flags);
+
+-- output the decoded flag HEAP_XMIN_FROZEN instead
+SELECT t_infomask, t_infomask2, flags
+FROM heap_page_items(get_raw_page('test1', 0)),
+ LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2, true) m(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);
+-- 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);
+-- 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))
+ 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;
+
+-- no flags
+SELECT unnest(heap_tuple_infomask_flags(0, 0, false));
+SELECT unnest(heap_tuple_infomask_flags(0, 0, true));
+
DROP TABLE test1;
-- check that using any of these functions with a partitioned table or index
<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 flag bits of <structfield>t_infomask</structfield>
+ and <structfield>t_infomask2</structfield> for heap tuples.
+ </para>
</listitem>
</varlistentry>
</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. 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))
+ 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>.
+ </para>
+ <para>
+ See <filename>src/include/access/htup_details.h</filename> for
+ explanations of the flag names returned.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</sect2>