From ff32629101f326bcd66f9c24c03537982c636c01 Mon Sep 17 00:00:00 2001 From: Shinya Kato Date: Mon, 9 Feb 2026 13:35:07 +0900 Subject: [PATCH v2 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 Reviewed-by: Discussion: https://postgr.es/m/CAOzEurQetW=-1+OnMo8baeVQF=-kAr-wNtFcgRNo+ErPk=xsDQ@mail.gmail.com --- src/backend/catalog/system_functions.sql | 6 +++ src/backend/utils/adt/xid.c | 64 ++++++++++++++++++++++++ src/include/catalog/pg_operator.dat | 12 +++++ src/include/catalog/pg_proc.dat | 13 +++++ src/test/regress/expected/xid.out | 46 +++++++++++++++++ src/test/regress/sql/xid.sql | 14 ++++++ 6 files changed, 155 insertions(+) diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql index eb9e31ae1bf..7aee4e3e65d 100644 --- a/src/backend/catalog/system_functions.sql +++ b/src/backend/catalog/system_functions.sql @@ -103,6 +103,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..2b770e011cd 100644 --- a/src/backend/utils/adt/xid.c +++ b/src/backend/utils/adt/xid.c @@ -312,6 +312,70 @@ 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 result; + + result = val + (uint64) delta; + + /* Check for over/underflow */ + if ((delta > 0 && result < val) || (delta < 0 && result > val)) + 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 result; + + result = val - (uint64) delta; + + /* Check for over/underflow */ + if ((delta > 0 && result > val) || (delta < 0 && result < val)) + 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 + { + if (val2 - val1 > (uint64) PG_INT64_MAX + 1) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("bigint out of range"))); + PG_RETURN_INT64(-((int64) (val2 - val1))); + } +} + Datum xid8_larger(PG_FUNCTION_ARGS) { diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat index 1465f13120a..4cc38e7e294 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 83f6501df38..f9c9690a376 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..4d5f4006072 100644 --- a/src/test/regress/expected/xid.out +++ b/src/test/regress/expected/xid.out @@ -175,6 +175,52 @@ 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 -- 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..f6bacc04428 100644 --- a/src/test/regress/sql/xid.sql +++ b/src/test/regress/sql/xid.sql @@ -59,6 +59,20 @@ 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; + -- pg_snapshot data type and related functions -- 2.47.3