Add tid_block() and tid_offset() accessor functions

The two new functions allow to extract the block number and offset from a tid.

There are existing ways to do so (e.g. by doing (ctid::text::point)[0]), but
they are hard to remember and not pretty.

tid_block() returns int8 (bigint) because BlockNumber is uint32, which exceeds
the range of int4. tid_offset() returns int4 (integer) because OffsetNumber is
uint16, which fits safely in int4.

Bumps catversion.

Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://postgr.es/m/CAJTYsWUzok2+mvSYkbVUwq_SWWg-GdHqCuYumN82AU97SjwjCA@mail.gmail.com
This commit is contained in:
Andres Freund 2026-04-05 14:45:27 -04:00
parent f10b6be258
commit df6949ccf7
9 changed files with 197 additions and 1 deletions

View file

@ -17,6 +17,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY func-formatting SYSTEM "func-formatting.sgml">
<!ENTITY func-datetime SYSTEM "func-datetime.sgml">
<!ENTITY func-enum SYSTEM "func-enum.sgml">
<!ENTITY func-tid SYSTEM "func-tid.sgml">
<!ENTITY func-geometry SYSTEM "func-geometry.sgml">
<!ENTITY func-net SYSTEM "func-net.sgml">
<!ENTITY func-textsearch SYSTEM "func-textsearch.sgml">

View file

@ -0,0 +1,70 @@
<sect1 id="functions-tid">
<title>TID Functions</title>
<indexterm zone="functions-tid">
<primary>TID</primary>
<secondary>functions</secondary>
</indexterm>
<indexterm>
<primary>tid_block</primary>
</indexterm>
<indexterm>
<primary>tid_offset</primary>
</indexterm>
<para>
<xref linkend="functions-tid-table"/> lists functions for
the <type>tid</type> data type (tuple identifier).
</para>
<table id="functions-tid-table">
<title><acronym>TID</acronym> Functions</title>
<tgroup cols="1">
<thead>
<row>
<entry role="func_table_entry"><para role="func_signature">
Function
</para>
<para>
Description
</para>
<para>
Example(s)
</para></entry>
</row>
</thead>
<tbody>
<row>
<entry role="func_table_entry"><para role="func_signature">
<function>tid_block</function> ( <type>tid</type> )
<returnvalue>bigint</returnvalue>
</para>
<para>
Extracts the block number from a tuple identifier.
</para>
<para>
<literal>tid_block('(42,7)'::tid)</literal>
<returnvalue>42</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<function>tid_offset</function> ( <type>tid</type> )
<returnvalue>integer</returnvalue>
</para>
<para>
Extracts the tuple offset within the block from a tuple identifier.
</para>
<para>
<literal>tid_offset('(42,7)'::tid)</literal>
<returnvalue>7</returnvalue>
</para></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>

View file

@ -62,6 +62,7 @@ repeat('Pg', 4) <returnvalue>PgPgPgPg</returnvalue>
&func-geometry;
&func-net;
&func-textsearch;
&func-tid;
&func-uuid;
&func-xml;
&func-json;

View file

@ -279,6 +279,35 @@ hashtidextended(PG_FUNCTION_ARGS)
seed);
}
/*
* Extract the block number from a TID
*
* Returns int8 because BlockNumber is uint32, which exceeds the range of int4.
*/
Datum
tid_block(PG_FUNCTION_ARGS)
{
ItemPointer tid = PG_GETARG_ITEMPOINTER(0);
/* need to use NoCheck, as tidin allows InvalidBlockNumber */
PG_RETURN_INT64((int64) ItemPointerGetBlockNumberNoCheck(tid));
}
/*
* Extract the offset number from a TID
*
* Returns int4 because OffsetNumber is uint16, which exceeds the range of
* int2.
*/
Datum
tid_offset(PG_FUNCTION_ARGS)
{
ItemPointer tid = PG_GETARG_ITEMPOINTER(0);
/* need to use NoCheck, as tidin allows InvalidOffsetNumber */
PG_RETURN_INT32((int32) ItemPointerGetOffsetNumberNoCheck(tid));
}
/*
* Functions to get latest tid of a specified tuple.

View file

@ -57,6 +57,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202604031
#define CATALOG_VERSION_NO 202604051
#endif

View file

@ -2757,6 +2757,12 @@
{ oid => '2796', descr => 'smaller of two',
proname => 'tidsmaller', prorettype => 'tid', proargtypes => 'tid tid',
prosrc => 'tidsmaller' },
{ oid => '9951', descr => 'extract block number from tid',
proname => 'tid_block', proleakproof => 't', prorettype => 'int8',
proargtypes => 'tid', prosrc => 'tid_block' },
{ oid => '9952', descr => 'extract offset number from tid',
proname => 'tid_offset', proleakproof => 't', prorettype => 'int4',
proargtypes => 'tid', prosrc => 'tid_offset' },
{ oid => '2233', descr => 'hash',
proname => 'hashtid', prorettype => 'int4', proargtypes => 'tid',
prosrc => 'hashtid' },

View file

@ -887,6 +887,8 @@ oid8le(oid8,oid8)
oid8gt(oid8,oid8)
oid8ge(oid8,oid8)
btoid8cmp(oid8,oid8)
tid_block(tid)
tid_offset(tid)
-- Check that functions without argument are not marked as leakproof.
SELECT p1.oid::regprocedure
FROM pg_proc p1 JOIN pg_namespace pn

View file

@ -42,6 +42,57 @@ SELECT * FROM pg_input_error_info('(0,-1)', 'tid');
invalid input syntax for type tid: "(0,-1)" | | | 22P02
(1 row)
-- tests for tid_block() and tid_offset()
SELECT tid_block('(0,0)'::tid), tid_offset('(0,0)'::tid);
tid_block | tid_offset
-----------+------------
0 | 0
(1 row)
SELECT tid_block('(0,1)'::tid), tid_offset('(0,1)'::tid);
tid_block | tid_offset
-----------+------------
0 | 1
(1 row)
SELECT tid_block('(42,7)'::tid), tid_offset('(42,7)'::tid);
tid_block | tid_offset
-----------+------------
42 | 7
(1 row)
-- max values: blockno uint32 max, offset uint16 max
SELECT tid_block('(4294967295,65535)'::tid), tid_offset('(4294967295,65535)'::tid);
tid_block | tid_offset
------------+------------
4294967295 | 65535
(1 row)
-- (-1,0) wraps to blockno 4294967295
SELECT tid_block('(-1,0)'::tid);
tid_block
------------
4294967295
(1 row)
-- NULL handling (strict functions)
SELECT tid_block(NULL::tid), tid_offset(NULL::tid);
tid_block | tid_offset
-----------+------------
|
(1 row)
-- round-trip: blockno + offset reconstruct the original TID
SELECT t, tid_block(t), tid_offset(t),
format('(%s,%s)', tid_block(t), tid_offset(t))::tid = t AS roundtrip_ok
FROM (VALUES ('(0,0)'::tid), ('(1,42)'::tid), ('(4294967295,65535)'::tid)) AS v(t);
t | tid_block | tid_offset | roundtrip_ok
--------------------+------------+------------+--------------
(0,0) | 0 | 0 | t
(1,42) | 1 | 42 | t
(4294967295,65535) | 4294967295 | 65535 | t
(3 rows)
-- tests for functions related to TID handling
CREATE TABLE tid_tab (a int);
-- min() and max() for TIDs
@ -58,6 +109,21 @@ SELECT max(ctid) FROM tid_tab;
(0,2)
(1 row)
-- tid_block() and tid_offset() with real table ctid
SELECT ctid, tid_block(ctid), tid_offset(ctid) FROM tid_tab;
ctid | tid_block | tid_offset
-------+-----------+------------
(0,1) | 0 | 1
(0,2) | 0 | 2
(2 rows)
-- use in WHERE clause
SELECT ctid FROM tid_tab WHERE tid_block(ctid) = 0 AND tid_offset(ctid) = 1;
ctid
-------
(0,1)
(1 row)
TRUNCATE tid_tab;
-- Tests for currtid2() with various relation kinds
-- Materialized view

View file

@ -16,6 +16,22 @@ SELECT pg_input_is_valid('(0,-1)', 'tid');
SELECT * FROM pg_input_error_info('(0,-1)', 'tid');
-- tests for tid_block() and tid_offset()
SELECT tid_block('(0,0)'::tid), tid_offset('(0,0)'::tid);
SELECT tid_block('(0,1)'::tid), tid_offset('(0,1)'::tid);
SELECT tid_block('(42,7)'::tid), tid_offset('(42,7)'::tid);
-- max values: blockno uint32 max, offset uint16 max
SELECT tid_block('(4294967295,65535)'::tid), tid_offset('(4294967295,65535)'::tid);
-- (-1,0) wraps to blockno 4294967295
SELECT tid_block('(-1,0)'::tid);
-- NULL handling (strict functions)
SELECT tid_block(NULL::tid), tid_offset(NULL::tid);
-- round-trip: blockno + offset reconstruct the original TID
SELECT t, tid_block(t), tid_offset(t),
format('(%s,%s)', tid_block(t), tid_offset(t))::tid = t AS roundtrip_ok
FROM (VALUES ('(0,0)'::tid), ('(1,42)'::tid), ('(4294967295,65535)'::tid)) AS v(t);
-- tests for functions related to TID handling
CREATE TABLE tid_tab (a int);
@ -24,6 +40,11 @@ CREATE TABLE tid_tab (a int);
INSERT INTO tid_tab VALUES (1), (2);
SELECT min(ctid) FROM tid_tab;
SELECT max(ctid) FROM tid_tab;
-- tid_block() and tid_offset() with real table ctid
SELECT ctid, tid_block(ctid), tid_offset(ctid) FROM tid_tab;
-- use in WHERE clause
SELECT ctid FROM tid_tab WHERE tid_block(ctid) = 0 AND tid_offset(ctid) = 1;
TRUNCATE tid_tab;
-- Tests for currtid2() with various relation kinds