From 1f1108881de4a95d870073ead4dc97d8d041c0e1 Mon Sep 17 00:00:00 2001 From: Masahiko Sawada 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)); src/include/access/htup_details.h for explanations of the fields returned. + + The heap_infomask_flags function can be used to unpack the + recognised bits of the infomasks of heap tuples. + @@ -236,6 +240,35 @@ test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class + + + + heap_infomask_flags(infomask integer, infomask2 integer, decode_combined bool) returns text[] + + heap_infomask_flags + + + + + heap_infomask_flags decodes the + t_infomask and + t_infomask2 returned by + heap_page_items into a human-readable + array of flag names. This can be used to see the tuple hint bits etc. + + + If decode_combined is set, combination flags like + HEAP_XMIN_FROZEN are output instead of raw + flags, HEAP_XMIN_COMMITTED and + HEAP_XMIN_INVALID. Default value is + false. + + + For the meaning of these flags see + src/include/access/htup_details.h + + + -- 2.10.5