From 27e476f2b52c82efc4f545a198f75df704992b9a Mon Sep 17 00:00:00 2001 From: Shinya Kato 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 Reviewed-by: lin teletele 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 for more details. + + The xid8 type supports the standard comparison operators, + like = and >. Two + xid8 values can be subtracted using the - + operator; the result is the signed distance between them as a + bigint. A bigint can also be added to or + subtracted from an xid8 using the + +(xid8,bigint), +(bigint,xid8), + and -(xid8,bigint) operators, respectively. Note + that the calculated xid8 should be in the range of the + xid8 type, i.e., between 0 and + 18446744073709551615, and that subtracting two + xid8 values yielding a result outside the range of + bigint raises an error. + + A third identifier type used by the system is cid, 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