]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Add tid_block() and tid_offset() accessor functions
authorAndres Freund <andres@anarazel.de>
Sun, 5 Apr 2026 18:45:27 +0000 (14:45 -0400)
committerAndres Freund <andres@anarazel.de>
Sun, 5 Apr 2026 19:17:05 +0000 (15:17 -0400)
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

doc/src/sgml/func/allfiles.sgml
doc/src/sgml/func/func-tid.sgml [new file with mode: 0644]
doc/src/sgml/func/func.sgml
src/backend/utils/adt/tid.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/test/regress/expected/opr_sanity.out
src/test/regress/expected/tid.out
src/test/regress/sql/tid.sql

index ce11ef1d5d8edd05071778ae99250d271f83da58..f5e3f0085378c294d265ff0253bd764aa7a23cbb 100644 (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">
diff --git a/doc/src/sgml/func/func-tid.sgml b/doc/src/sgml/func/func-tid.sgml
new file mode 100644 (file)
index 0000000..188e66a
--- /dev/null
@@ -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>
index f351ef53f63d41d38e5a72f5f6c4b4374fb3aa77..c9c231dd190224d4b0b77f2b61c913e8eef3afd3 100644 (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;
index 07248b69e57ab20cc4165690e0c33090cf57f5f9..9257886f1dad2140c0908d6ae30607140c654271 100644 (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.
index 582bb2e20585904ade8a8bbc2bdad7131aef9838..0cee5315b59155c3b97c8841eec173ad60389007 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202604031
+#define CATALOG_VERSION_NO     202604051
 
 #endif
index cbf85d6a5be0f1df5201df9853a71890cbfb8d6c..3ea17fc562956cb93e664ff838eb5776be8744dd 100644 (file)
 { 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' },
index 6ff4d7ee90145a1b9d776bac07bcb6bc3d8d839c..cfdc6b1a17a2513fb11e69b59e22f5e3fb56aae6 100644 (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
index 083c83a1e1b2ceb2581a48bb128499c61dbd3d4f..3497a77688b5130e1111dd739fa819126ee006bf 100644 (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
index 2602e20eb5a1f4c26f2bcf2afb6c098b0decb0ee..c0a70be5cbdeb1245a7fb14570fe24a4f3a7eb73 100644 (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