From: Andres Freund Date: Sun, 5 Apr 2026 18:45:27 +0000 (-0400) Subject: Add tid_block() and tid_offset() accessor functions X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=df6949ccf7a6d5160e3068ed843eb46ab7bc2601;p=thirdparty%2Fpostgresql.git 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 Discussion: https://postgr.es/m/CAJTYsWUzok2+mvSYkbVUwq_SWWg-GdHqCuYumN82AU97SjwjCA@mail.gmail.com --- diff --git a/doc/src/sgml/func/allfiles.sgml b/doc/src/sgml/func/allfiles.sgml index ce11ef1d5d8..f5e3f008537 100644 --- a/doc/src/sgml/func/allfiles.sgml +++ b/doc/src/sgml/func/allfiles.sgml @@ -17,6 +17,7 @@ Complete list of usable sgml source files in this directory. + diff --git a/doc/src/sgml/func/func-tid.sgml b/doc/src/sgml/func/func-tid.sgml new file mode 100644 index 00000000000..188e66a181f --- /dev/null +++ b/doc/src/sgml/func/func-tid.sgml @@ -0,0 +1,70 @@ + + TID Functions + + + TID + functions + + + + tid_block + + + + tid_offset + + + + lists functions for + the tid data type (tuple identifier). + + + + <acronym>TID</acronym> Functions + + + + + Function + + + Description + + + Example(s) + + + + + + + + tid_block ( tid ) + bigint + + + Extracts the block number from a tuple identifier. + + + tid_block('(42,7)'::tid) + 42 + + + + + + tid_offset ( tid ) + integer + + + Extracts the tuple offset within the block from a tuple identifier. + + + tid_offset('(42,7)'::tid) + 7 + + + + +
+
diff --git a/doc/src/sgml/func/func.sgml b/doc/src/sgml/func/func.sgml index f351ef53f63..c9c231dd190 100644 --- a/doc/src/sgml/func/func.sgml +++ b/doc/src/sgml/func/func.sgml @@ -62,6 +62,7 @@ repeat('Pg', 4) PgPgPgPg &func-geometry; &func-net; &func-textsearch; +&func-tid; &func-uuid; &func-xml; &func-json; diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c index 07248b69e57..9257886f1da 100644 --- a/src/backend/utils/adt/tid.c +++ b/src/backend/utils/adt/tid.c @@ -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. diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 582bb2e2058..0cee5315b59 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202604031 +#define CATALOG_VERSION_NO 202604051 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index cbf85d6a5be..3ea17fc5629 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -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' }, diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 6ff4d7ee901..cfdc6b1a17a 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -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 diff --git a/src/test/regress/expected/tid.out b/src/test/regress/expected/tid.out index 083c83a1e1b..3497a77688b 100644 --- a/src/test/regress/expected/tid.out +++ b/src/test/regress/expected/tid.out @@ -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 diff --git a/src/test/regress/sql/tid.sql b/src/test/regress/sql/tid.sql index 2602e20eb5a..c0a70be5cbd 100644 --- a/src/test/regress/sql/tid.sql +++ b/src/test/regress/sql/tid.sql @@ -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