--CREATE EXTENSION pageinspect;
create table t_test (id int, content text);
insert into t_test values (1, 'test');
update t_test set content='test1' where id=1;

--sql for inspect the page
SELECT t_xmin, t_xmax, t_ctid, raw_flags, combined_flags FROM heap_page_items(get_raw_page('t_test', 0)), 
LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2)
WHERE t_infomask IS NOT NULL OR t_infomask2 IS NOT NULL;

--xmax on the new tuple is 0 as expected
 t_xmin | t_xmax | t_ctid |                             raw_flags                             | combined_flags
--------+--------+--------+-------------------------------------------------------------------+----------------
    751 |    752 | (0,2)  | {HEAP_HASVARWIDTH,HEAP_XMIN_COMMITTED,HEAP_HOT_UPDATED}           | {}
    752 |      0 | (0,2)  | {HEAP_HASVARWIDTH,HEAP_XMAX_INVALID,HEAP_UPDATED,HEAP_ONLY_TUPLE} | {}
(2 rows)

--create a before row update trigger on t_test
CREATE OR REPLACE FUNCTION before_t_test()
RETURNS TRIGGER AS $$
BEGIN
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tr_before_t_test_update
BEFORE UPDATE ON t_test
FOR EACH ROW
EXECUTE FUNCTION before_t_test();

--update the same row again
update t_test set content='test2' where id=1;

--the xmax in the new tuple is equal to xmin
--and HEAP_XMAX_KEYSHR_LOCK is set
 t_xmin | t_xmax | t_ctid |                                         raw_flags                                         | combined_flags
--------+--------+--------+-------------------------------------------------------------------------------------------+----------------
    751 |    752 | (0,2)  | {HEAP_HASVARWIDTH,HEAP_XMIN_COMMITTED,HEAP_XMAX_COMMITTED,HEAP_HOT_UPDATED}               | {}
    752 |    755 | (0,3)  | {HEAP_HASVARWIDTH,HEAP_XMIN_COMMITTED,HEAP_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE}      | {}
    755 |    755 | (0,3)  | {HEAP_HASVARWIDTH,HEAP_XMAX_KEYSHR_LOCK,HEAP_XMAX_LOCK_ONLY,HEAP_UPDATED,HEAP_ONLY_TUPLE} | {}
(3 rows)

--update the same row with two subtransactions
begin;
savepoint sp1;
update t_test set content='test3' where id=1;
savepoint sp2;
update t_test set content='test4' where id=1;
commit;

--two new tuples are inserted with xmax set to multixact id
--and HEAP_XMAX_EXCL_LOCK is set
 t_xmin | t_xmax | t_ctid |                                                       raw_flags                                                       | combined_flags
--------+--------+--------+-----------------------------------------------------------------------------------------------------------------------+----------------
    751 |    752 | (0,2)  | {HEAP_HASVARWIDTH,HEAP_XMIN_COMMITTED,HEAP_XMAX_COMMITTED,HEAP_HOT_UPDATED}                                           | {}
    752 |    755 | (0,3)  | {HEAP_HASVARWIDTH,HEAP_XMIN_COMMITTED,HEAP_XMAX_COMMITTED,HEAP_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE}              | {}
    755 |    757 | (0,4)  | {HEAP_HASVARWIDTH,HEAP_XMIN_COMMITTED,HEAP_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE}                                  | {}
    757 |      4 | (0,5)  | {HEAP_HASVARWIDTH,HEAP_COMBOCID,HEAP_XMAX_EXCL_LOCK,HEAP_XMAX_IS_MULTI,HEAP_UPDATED,HEAP_HOT_UPDATED,HEAP_ONLY_TUPLE} | {}
    758 |      3 | (0,5)  | {HEAP_HASVARWIDTH,HEAP_XMAX_EXCL_LOCK,HEAP_XMAX_LOCK_ONLY,HEAP_XMAX_IS_MULTI,HEAP_UPDATED,HEAP_ONLY_TUPLE}            | {}
(5 rows)