v5-0001-Add-arithmetic-operators-for-xid8.patch
application/octet-stream
Filename: v5-0001-Add-arithmetic-operators-for-xid8.patch
Type: application/octet-stream
Part: 0
From 27e476f2b52c82efc4f545a198f75df704992b9a Mon Sep 17 00:00:00 2001
From: Shinya Kato <shinya11.kato@gmail.com>
Date: Mon, 9 Feb 2026 13:35:07 +0900
Subject: [PATCH v5 1/2] Add arithmetic operators for xid8
Add +, - operators for xid8 type to allow direct arithmetic
without the need for casting through text and bigint:
xid8 + int8 -> xid8
int8 + xid8 -> xid8
xid8 - int8 -> xid8
xid8 - xid8 -> int8
These operators follow the same pattern as the existing pg_lsn
arithmetic operators. Since there are no implicit casts between
xid8 and any ordinary numeric type, this avoids the "ambiguous
operator" concern.
Author: Shinya Kato <shinya11.kato@gmail.com>
Reviewed-by: lin teletele <teletele.lin@gmail.com>
Discussion: https://postgr.es/m/CAOzEurQetW=-1+OnMo8baeVQF=-kAr-wNtFcgRNo+ErPk=xsDQ@mail.gmail.com
---
doc/src/sgml/datatype.sgml | 16 +++++
src/backend/catalog/system_functions.sql | 6 ++
src/backend/utils/adt/xid.c | 77 ++++++++++++++++++++++++
src/include/catalog/pg_operator.dat | 12 ++++
src/include/catalog/pg_proc.dat | 13 ++++
src/test/regress/expected/xid.out | 73 ++++++++++++++++++++++
src/test/regress/sql/xid.sql | 21 +++++++
7 files changed, 218 insertions(+)
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index d8d91678e86..28362fae80b 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -5096,6 +5096,22 @@ WHERE ...
cluster. See <xref linkend="transaction-id"/> for more details.
</para>
+ <para>
+ The <type>xid8</type> type supports the standard comparison operators,
+ like <literal>=</literal> and <literal>></literal>. Two
+ <type>xid8</type> values can be subtracted using the <literal>-</literal>
+ operator; the result is the signed distance between them as a
+ <type>bigint</type>. A <type>bigint</type> can also be added to or
+ subtracted from an <type>xid8</type> using the
+ <literal>+(xid8,bigint)</literal>, <literal>+(bigint,xid8)</literal>,
+ and <literal>-(xid8,bigint)</literal> operators, respectively. Note
+ that the calculated <type>xid8</type> should be in the range of the
+ <type>xid8</type> type, i.e., between <literal>0</literal> and
+ <literal>18446744073709551615</literal>, and that subtracting two
+ <type>xid8</type> values yielding a result outside the range of
+ <type>bigint</type> raises an error.
+ </para>
+
<para>
A third identifier type used by the system is <type>cid</type>, or
command identifier. This is the data type of the system columns
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index c3c0a6e84ed..1f9e889115d 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -93,6 +93,12 @@ CREATE OR REPLACE FUNCTION numeric_pl_pg_lsn(numeric, pg_lsn)
IMMUTABLE PARALLEL SAFE STRICT COST 1
RETURN $2 + $1;
+CREATE OR REPLACE FUNCTION int8_pl_xid8(bigint, xid8)
+ RETURNS xid8
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
CREATE OR REPLACE FUNCTION path_contain_pt(path, point)
RETURNS boolean
LANGUAGE sql
diff --git a/src/backend/utils/adt/xid.c b/src/backend/utils/adt/xid.c
index f746a5f97dd..b47880e6945 100644
--- a/src/backend/utils/adt/xid.c
+++ b/src/backend/utils/adt/xid.c
@@ -312,6 +312,83 @@ hashxid8extended(PG_FUNCTION_ARGS)
return hashint8extended(fcinfo);
}
+Datum
+xid8pl(PG_FUNCTION_ARGS)
+{
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
+ int64 delta = PG_GETARG_INT64(1);
+ uint64 val = U64FromFullTransactionId(fxid);
+ uint64 abs_delta = pg_abs_s64(delta);
+ uint64 result;
+ bool overflow;
+
+ if (delta >= 0)
+ overflow = pg_add_u64_overflow(val, abs_delta, &result);
+ else
+ overflow = pg_sub_u64_overflow(val, abs_delta, &result);
+
+ if (overflow)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("xid8 out of range")));
+
+ PG_RETURN_FULLTRANSACTIONID(FullTransactionIdFromU64(result));
+}
+
+Datum
+xid8mi(PG_FUNCTION_ARGS)
+{
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
+ int64 delta = PG_GETARG_INT64(1);
+ uint64 val = U64FromFullTransactionId(fxid);
+ uint64 abs_delta = pg_abs_s64(delta);
+ uint64 result;
+ bool overflow;
+
+ if (delta >= 0)
+ overflow = pg_sub_u64_overflow(val, abs_delta, &result);
+ else
+ overflow = pg_add_u64_overflow(val, abs_delta, &result);
+
+ if (overflow)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("xid8 out of range")));
+
+ PG_RETURN_FULLTRANSACTIONID(FullTransactionIdFromU64(result));
+}
+
+Datum
+xid8_mi_xid8(PG_FUNCTION_ARGS)
+{
+ FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
+ FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1);
+ uint64 val1 = U64FromFullTransactionId(fxid1);
+ uint64 val2 = U64FromFullTransactionId(fxid2);
+
+ if (val1 >= val2)
+ {
+ if (val1 - val2 > (uint64) PG_INT64_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+ PG_RETURN_INT64((int64) (val1 - val2));
+ }
+ else
+ {
+ uint64 diff = val2 - val1;
+
+ if (diff > (uint64) PG_INT64_MAX + 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+ /* diff == 2^63 maps to PG_INT64_MIN without signed overflow */
+ if (diff > (uint64) PG_INT64_MAX)
+ PG_RETURN_INT64(PG_INT64_MIN);
+ PG_RETURN_INT64(-(int64) diff);
+ }
+}
+
Datum
xid8_larger(PG_FUNCTION_ARGS)
{
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 1a8fd8b8645..453c4938e62 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -219,6 +219,18 @@
oprname => '>=', oprleft => 'xid8', oprright => 'xid8', oprresult => 'bool',
oprcom => '<=(xid8,xid8)', oprnegate => '<(xid8,xid8)', oprcode => 'xid8ge',
oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '5107', descr => 'add',
+ oprname => '+', oprleft => 'xid8', oprright => 'int8', oprresult => 'xid8',
+ oprcom => '+(int8,xid8)', oprcode => 'xid8pl' },
+{ oid => '5108', descr => 'add',
+ oprname => '+', oprleft => 'int8', oprright => 'xid8', oprresult => 'xid8',
+ oprcom => '+(xid8,int8)', oprcode => 'int8_pl_xid8' },
+{ oid => '5109', descr => 'subtract',
+ oprname => '-', oprleft => 'xid8', oprright => 'int8', oprresult => 'xid8',
+ oprcode => 'xid8mi' },
+{ oid => '5110', descr => 'subtract',
+ oprname => '-', oprleft => 'xid8', oprright => 'xid8', oprresult => 'int8',
+ oprcode => 'xid8_mi_xid8' },
{ oid => '385', descr => 'equal',
oprname => '=', oprcanhash => 't', oprleft => 'cid', oprright => 'cid',
oprresult => 'bool', oprcom => '=(cid,cid)', oprcode => 'cideq',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index be157a5fbe9..e5c080a310f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -209,6 +209,19 @@
{ oid => '5098', descr => 'smaller of two',
proname => 'xid8_smaller', prorettype => 'xid8', proargtypes => 'xid8 xid8',
prosrc => 'xid8_smaller' },
+{ oid => '5101',
+ proname => 'xid8pl', prorettype => 'xid8', proargtypes => 'xid8 int8',
+ prosrc => 'xid8pl' },
+{ oid => '5102',
+ proname => 'xid8mi', prorettype => 'xid8', proargtypes => 'xid8 int8',
+ prosrc => 'xid8mi' },
+{ oid => '5103',
+ proname => 'xid8_mi_xid8', prorettype => 'int8', proargtypes => 'xid8 xid8',
+ prosrc => 'xid8_mi_xid8' },
+{ oid => '5106',
+ proname => 'int8_pl_xid8', prolang => 'sql',
+ prorettype => 'xid8', proargtypes => 'int8 xid8',
+ prosrc => 'see system_functions.sql' },
{ oid => '69',
proname => 'cideq', proleakproof => 't', prorettype => 'bool',
proargtypes => 'cid cid', prosrc => 'cideq' },
diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out
index 1ce7826cf90..ba1781b9ee6 100644
--- a/src/test/regress/expected/xid.out
+++ b/src/test/regress/expected/xid.out
@@ -175,6 +175,79 @@ select min(x), max(x) from xid8_t1;
create index on xid8_t1 using btree(x);
create index on xid8_t1 using hash(x);
drop table xid8_t1;
+-- xid8 arithmetic operators
+select '42'::xid8 + 3::bigint;
+ ?column?
+----------
+ 45
+(1 row)
+
+select 3::bigint + '42'::xid8;
+ ?column?
+----------
+ 45
+(1 row)
+
+select '42'::xid8 - 3::bigint;
+ ?column?
+----------
+ 39
+(1 row)
+
+select '100'::xid8 - '42'::xid8;
+ ?column?
+----------
+ 58
+(1 row)
+
+select '42'::xid8 + (-3)::bigint;
+ ?column?
+----------
+ 39
+(1 row)
+
+select '42'::xid8 - (-3)::bigint;
+ ?column?
+----------
+ 45
+(1 row)
+
+-- xid8 arithmetic overflow/underflow
+select '0'::xid8 - 1::bigint;
+ERROR: xid8 out of range
+select '18446744073709551615'::xid8 + 1::bigint;
+ERROR: xid8 out of range
+select '18446744073709551615'::xid8 - '0'::xid8;
+ERROR: bigint out of range
+select '0'::xid8 - '18446744073709551615'::xid8;
+ERROR: bigint out of range
+-- xid8 arithmetic at int8 boundaries
+select '0'::xid8 + 9223372036854775807::bigint;
+ ?column?
+---------------------
+ 9223372036854775807
+(1 row)
+
+select '0'::xid8 - (-9223372036854775807 - 1)::bigint;
+ ?column?
+---------------------
+ 9223372036854775808
+(1 row)
+
+select '9223372036854775807'::xid8 - (-9223372036854775807 - 1)::bigint;
+ ?column?
+----------------------
+ 18446744073709551615
+(1 row)
+
+select '0'::xid8 - '9223372036854775808'::xid8;
+ ?column?
+----------------------
+ -9223372036854775808
+(1 row)
+
+select '0'::xid8 - '9223372036854775809'::xid8;
+ERROR: bigint out of range
-- pg_snapshot data type and related functions
-- Note: another set of tests similar to this exists in txid.sql, for a limited
-- time (the relevant functions share C code)
diff --git a/src/test/regress/sql/xid.sql b/src/test/regress/sql/xid.sql
index 9f716b3653a..77d45797579 100644
--- a/src/test/regress/sql/xid.sql
+++ b/src/test/regress/sql/xid.sql
@@ -59,6 +59,27 @@ create index on xid8_t1 using btree(x);
create index on xid8_t1 using hash(x);
drop table xid8_t1;
+-- xid8 arithmetic operators
+select '42'::xid8 + 3::bigint;
+select 3::bigint + '42'::xid8;
+select '42'::xid8 - 3::bigint;
+select '100'::xid8 - '42'::xid8;
+select '42'::xid8 + (-3)::bigint;
+select '42'::xid8 - (-3)::bigint;
+
+-- xid8 arithmetic overflow/underflow
+select '0'::xid8 - 1::bigint;
+select '18446744073709551615'::xid8 + 1::bigint;
+select '18446744073709551615'::xid8 - '0'::xid8;
+select '0'::xid8 - '18446744073709551615'::xid8;
+
+-- xid8 arithmetic at int8 boundaries
+select '0'::xid8 + 9223372036854775807::bigint;
+select '0'::xid8 - (-9223372036854775807 - 1)::bigint;
+select '9223372036854775807'::xid8 - (-9223372036854775807 - 1)::bigint;
+select '0'::xid8 - '9223372036854775808'::xid8;
+select '0'::xid8 - '9223372036854775809'::xid8;
+
-- pg_snapshot data type and related functions
--
2.47.3