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
<!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">
--- /dev/null
+ <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>
&func-geometry;
&func-net;
&func-textsearch;
+&func-tid;
&func-uuid;
&func-xml;
&func-json;
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.
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202604031
+#define CATALOG_VERSION_NO 202604051
#endif
{ 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' },
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
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
(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
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);
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