public inbox for [email protected]  
help / color / mirror / Atom feed
Re: Use pg_current_xact_id() instead of deprecated txid_current()
4+ messages / 2 participants
[nested] [flat]

* Re: Use pg_current_xact_id() instead of deprecated txid_current()
@ 2026-02-09 12:07 Shinya Kato <[email protected]>
  2026-02-10 06:38 ` Re: Use pg_current_xact_id() instead of deprecated txid_current() Shinya Kato <[email protected]>
  0 siblings, 1 reply; 4+ messages in thread

From: Shinya Kato @ 2026-02-09 12:07 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Álvaro Herrera <[email protected]>; pgsql-hackers <[email protected]>

On Mon, Feb 9, 2026 at 1:24 AM Tom Lane <[email protected]> wrote:
>
> =?UTF-8?Q?=C3=81lvaro_Herrera?= <[email protected]> writes:
> > On 2026-02-08, Shinya Kato wrote:
> >> Since pg_current_xact_id() returns xid8 which does not support
> >> arithmetic operators, places that need "xid + 1" cast the result via
> >> ::text::bigint first.
>
> > I think it may be better to add some operators, or was there a rationale for these not being there?
>
> I'm fairly concerned about overloading the arithmetic operators with
> unsigned versions.  The reason we never invented SQL-level uint8 and
> such is fear of getting a lot of "ambiguous operator" errors.  Now,
> if we are careful not to create implicit casts between xid[8] and
> any ordinary type, maybe it'd be okay to invent xid+int, xid8-int,
> and a few more.

Got it. I’ll give it a try, thanks.

> As things stand, I don't find the proposed patch to be an improvement.

I agree that casting xid8 to bigint was not the right approach.
However, I still believe it's important to move away from using
deprecated functions.

-- 
Best regards,
Shinya Kato
NTT OSS Center






^ permalink  raw  reply  [nested|flat] 4+ messages in thread

* Re: Use pg_current_xact_id() instead of deprecated txid_current()
  2026-02-09 12:07 Re: Use pg_current_xact_id() instead of deprecated txid_current() Shinya Kato <[email protected]>
@ 2026-02-10 06:38 ` Shinya Kato <[email protected]>
  2026-05-25 08:09   ` Re: Use pg_current_xact_id() instead of deprecated txid_current() Shinya Kato <[email protected]>
  0 siblings, 1 reply; 4+ messages in thread

From: Shinya Kato @ 2026-02-10 06:38 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Álvaro Herrera <[email protected]>; pgsql-hackers <[email protected]>

On Mon, Feb 9, 2026 at 9:07 PM Shinya Kato <[email protected]> wrote:
>
> On Mon, Feb 9, 2026 at 1:24 AM Tom Lane <[email protected]> wrote:
> >
> > =?UTF-8?Q?=C3=81lvaro_Herrera?= <[email protected]> writes:
> > > On 2026-02-08, Shinya Kato wrote:
> > >> Since pg_current_xact_id() returns xid8 which does not support
> > >> arithmetic operators, places that need "xid + 1" cast the result via
> > >> ::text::bigint first.
> >
> > > I think it may be better to add some operators, or was there a rationale for these not being there?
> >
> > I'm fairly concerned about overloading the arithmetic operators with
> > unsigned versions.  The reason we never invented SQL-level uint8 and
> > such is fear of getting a lot of "ambiguous operator" errors.  Now,
> > if we are careful not to create implicit casts between xid[8] and
> > any ordinary type, maybe it'd be okay to invent xid+int, xid8-int,
> > and a few more.
>
> Got it. I’ll give it a try, thanks.

I have added the + and - operators for the xid8 type in the v2-0001
patch. This allows for direct arithmetic and eliminates the need for
casting through text and bigint. Specifically, I implemented:

  xid8 + int8 -> xid8
  int8 + xid8 -> xid8
  xid8 - int8 -> xid8
  xid8 - xid8 -> int8

Additionally, the v2-0002 patch removes the existing ::text::bigint
casts where they are no longer necessary.

-- 
Best regards,
Shinya Kato
NTT OSS Center


Attachments:

  [application/octet-stream] v2-0001-Add-arithmetic-operators-for-xid8.patch (8.1K, 2-v2-0001-Add-arithmetic-operators-for-xid8.patch)
  download | inline diff:
From ff32629101f326bcd66f9c24c03537982c636c01 Mon Sep 17 00:00:00 2001
From: Shinya Kato <[email protected]>
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 <[email protected]>
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



  [application/octet-stream] v2-0002-Use-pg_current_xact_id-instead-of-deprecated-txid.patch (13.2K, 3-v2-0002-Use-pg_current_xact_id-instead-of-deprecated-txid.patch)
  download | inline diff:
From ce14e4386376cef4c0cc19ffa42c6ac2468ca225 Mon Sep 17 00:00:00 2001
From: Shinya Kato <[email protected]>
Date: Tue, 10 Feb 2026 13:54:01 +0900
Subject: [PATCH v2 2/2] Use pg_current_xact_id() instead of deprecated
 txid_current()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Replace txid_current() with pg_current_xact_id() in test code.
The previous commit added arithmetic operators for xid8, so
places that need "xid + 1" can now use pg_current_xact_id() + 1
directly.

Author: Shinya Kato <[email protected]>
Reviewed-by: Álvaro Herrera <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/CAOzEurQetW=-1+OnMo8baeVQF=-kAr-wNtFcgRNo+ErPk=xsDQ@mail.gmail.com
---
 contrib/pg_visibility/t/001_concurrent_transaction.pl     | 2 +-
 contrib/test_decoding/expected/slot_creation_error.out    | 6 +++---
 contrib/test_decoding/specs/slot_creation_error.spec      | 2 +-
 src/bin/pg_combinebackup/t/002_compare_backups.pl         | 4 ++--
 src/test/modules/commit_ts/expected/commit_timestamp.out  | 4 ++--
 .../modules/commit_ts/expected/commit_timestamp_1.out     | 4 ++--
 src/test/modules/commit_ts/sql/commit_timestamp.sql       | 4 ++--
 src/test/modules/xid_wraparound/t/004_notify_freeze.pl    | 4 ++--
 src/test/recovery/t/021_row_visibility.pl                 | 2 +-
 src/test/recovery/t/031_recovery_conflict.pl              | 2 +-
 src/test/recovery/t/040_standby_failover_slots_sync.pl    | 4 ++--
 src/test/regress/expected/create_index.out                | 2 +-
 src/test/regress/sql/create_index.sql                     | 2 +-
 src/test/subscription/t/035_conflicts.pl                  | 8 ++++----
 14 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/contrib/pg_visibility/t/001_concurrent_transaction.pl b/contrib/pg_visibility/t/001_concurrent_transaction.pl
index 3aa556892a6..889cae927fd 100644
--- a/contrib/pg_visibility/t/001_concurrent_transaction.pl
+++ b/contrib/pg_visibility/t/001_concurrent_transaction.pl
@@ -30,7 +30,7 @@ my $bsession = $node->background_psql('other_database');
 $bsession->query_safe(
 	qq[
 	BEGIN;
-	SELECT txid_current();
+	SELECT pg_current_xact_id();
 ]);
 
 # Create a sample table and run vacuum
diff --git a/contrib/test_decoding/expected/slot_creation_error.out b/contrib/test_decoding/expected/slot_creation_error.out
index 25883b508fb..95591752371 100644
--- a/contrib/test_decoding/expected/slot_creation_error.out
+++ b/contrib/test_decoding/expected/slot_creation_error.out
@@ -2,7 +2,7 @@ Parsed test spec with 2 sessions
 
 starting permutation: s1_b s1_xid s2_init s1_view_slot s1_cancel_s2 s1_view_slot s1_c
 step s1_b: BEGIN;
-step s1_xid: SELECT 'xid' FROM txid_current();
+step s1_xid: SELECT 'xid' FROM pg_current_xact_id();
 ?column?
 --------
 xid     
@@ -43,7 +43,7 @@ step s1_c: COMMIT;
 
 starting permutation: s1_b s1_xid s2_init s1_c s1_view_slot s1_drop_slot
 step s1_b: BEGIN;
-step s1_xid: SELECT 'xid' FROM txid_current();
+step s1_xid: SELECT 'xid' FROM pg_current_xact_id();
 ?column?
 --------
 xid     
@@ -78,7 +78,7 @@ pg_drop_replication_slot
 
 starting permutation: s1_b s1_xid s2_init s1_terminate_s2 s1_c s1_view_slot
 step s1_b: BEGIN;
-step s1_xid: SELECT 'xid' FROM txid_current();
+step s1_xid: SELECT 'xid' FROM pg_current_xact_id();
 ?column?
 --------
 xid     
diff --git a/contrib/test_decoding/specs/slot_creation_error.spec b/contrib/test_decoding/specs/slot_creation_error.spec
index d1e35bf58b5..6983f24eae6 100644
--- a/contrib/test_decoding/specs/slot_creation_error.spec
+++ b/contrib/test_decoding/specs/slot_creation_error.spec
@@ -4,7 +4,7 @@ session "s1"
 setup { SET synchronous_commit=on; }
 
 step s1_b { BEGIN; }
-step s1_xid { SELECT 'xid' FROM txid_current(); }
+step s1_xid { SELECT 'xid' FROM pg_current_xact_id(); }
 step s1_c { COMMIT; }
 step s1_cancel_s2 {
     SELECT pg_cancel_backend(pid)
diff --git a/src/bin/pg_combinebackup/t/002_compare_backups.pl b/src/bin/pg_combinebackup/t/002_compare_backups.pl
index b509296a94a..42d252b2242 100644
--- a/src/bin/pg_combinebackup/t/002_compare_backups.pl
+++ b/src/bin/pg_combinebackup/t/002_compare_backups.pl
@@ -105,9 +105,9 @@ my $lsn = $primary->safe_psql('postgres', "SELECT pg_current_wal_lsn();");
 
 # Make sure that the WAL segment containing that LSN has been archived.
 # PostgreSQL won't issue two consecutive XLOG_SWITCH records, and the backup
-# just issued one, so call txid_current() to generate some WAL activity
+# just issued one, so call pg_current_xact_id() to generate some WAL activity
 # before calling pg_switch_wal().
-$primary->safe_psql('postgres', 'SELECT txid_current();');
+$primary->safe_psql('postgres', 'SELECT pg_current_xact_id();');
 $primary->safe_psql('postgres', 'SELECT pg_switch_wal()');
 
 # Now wait for the LSN we chose above to be archived.
diff --git a/src/test/modules/commit_ts/expected/commit_timestamp.out b/src/test/modules/commit_ts/expected/commit_timestamp.out
index bb2fda27681..0d08e0684a2 100644
--- a/src/test/modules/commit_ts/expected/commit_timestamp.out
+++ b/src/test/modules/commit_ts/expected/commit_timestamp.out
@@ -71,7 +71,7 @@ SELECT * FROM pg_xact_commit_timestamp_origin('2'::xid); -- ok, NULL
 (1 row)
 
 -- Test transaction without replication origin
-SELECT txid_current() as txid_no_origin \gset
+SELECT pg_current_xact_id() as txid_no_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        roident != 0 AS valid_roident
@@ -104,7 +104,7 @@ SELECT pg_replication_origin_session_setup('regress_commit_ts: get_origin');
  
 (1 row)
 
-SELECT txid_current() as txid_with_origin \gset
+SELECT pg_current_xact_id() as txid_with_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        r.roname
diff --git a/src/test/modules/commit_ts/expected/commit_timestamp_1.out b/src/test/modules/commit_ts/expected/commit_timestamp_1.out
index f37e701f37a..21f09b89cac 100644
--- a/src/test/modules/commit_ts/expected/commit_timestamp_1.out
+++ b/src/test/modules/commit_ts/expected/commit_timestamp_1.out
@@ -63,7 +63,7 @@ SELECT * FROM pg_xact_commit_timestamp_origin('2'::xid); -- ok, NULL
 (1 row)
 
 -- Test transaction without replication origin
-SELECT txid_current() as txid_no_origin \gset
+SELECT pg_current_xact_id() as txid_no_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        roident != 0 AS valid_roident
@@ -90,7 +90,7 @@ SELECT pg_replication_origin_session_setup('regress_commit_ts: get_origin');
  
 (1 row)
 
-SELECT txid_current() as txid_with_origin \gset
+SELECT pg_current_xact_id() as txid_with_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        r.roname
diff --git a/src/test/modules/commit_ts/sql/commit_timestamp.sql b/src/test/modules/commit_ts/sql/commit_timestamp.sql
index 3bb7bb27a74..a2f0e68317d 100644
--- a/src/test/modules/commit_ts/sql/commit_timestamp.sql
+++ b/src/test/modules/commit_ts/sql/commit_timestamp.sql
@@ -34,7 +34,7 @@ SELECT * FROM pg_xact_commit_timestamp_origin('1'::xid); -- ok, NULL
 SELECT * FROM pg_xact_commit_timestamp_origin('2'::xid); -- ok, NULL
 
 -- Test transaction without replication origin
-SELECT txid_current() as txid_no_origin \gset
+SELECT pg_current_xact_id() as txid_no_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        roident != 0 AS valid_roident
@@ -48,7 +48,7 @@ SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
 SELECT pg_replication_origin_create('regress_commit_ts: get_origin') != 0
   AS valid_roident;
 SELECT pg_replication_origin_session_setup('regress_commit_ts: get_origin');
-SELECT txid_current() as txid_with_origin \gset
+SELECT pg_current_xact_id() as txid_with_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        r.roname
diff --git a/src/test/modules/xid_wraparound/t/004_notify_freeze.pl b/src/test/modules/xid_wraparound/t/004_notify_freeze.pl
index d0a1f1fe2fc..9a8300186f7 100644
--- a/src/test/modules/xid_wraparound/t/004_notify_freeze.pl
+++ b/src/test/modules/xid_wraparound/t/004_notify_freeze.pl
@@ -35,9 +35,9 @@ for my $i (1 .. 10)
 }
 
 # Consume enough XIDs to trigger truncation, and one more with
-# 'txid_current' to bump up the freeze horizon.
+# 'pg_current_xact_id' to bump up the freeze horizon.
 $node->safe_psql('postgres', 'select consume_xids(10000000);');
-$node->safe_psql('postgres', 'select txid_current()');
+$node->safe_psql('postgres', 'select pg_current_xact_id()');
 
 # Remember current datfrozenxid before vacuum freeze so that we can
 # check that it is advanced. (Taking the min() this way assumes that
diff --git a/src/test/recovery/t/021_row_visibility.pl b/src/test/recovery/t/021_row_visibility.pl
index 0a4d22b3698..9626d431852 100644
--- a/src/test/recovery/t/021_row_visibility.pl
+++ b/src/test/recovery/t/021_row_visibility.pl
@@ -94,7 +94,7 @@ UPDATE test_visibility SET data = 'first update' RETURNING data;
 		qr/^UPDATE 1$/m),
 	'UPDATE');
 
-$node_primary->psql('postgres', "SELECT txid_current();");  # ensure WAL flush
+$node_primary->psql('postgres', "SELECT pg_current_xact_id();");  # ensure WAL flush
 $node_primary->wait_for_catchup($node_standby);
 
 ok( send_query_and_wait(
diff --git a/src/test/recovery/t/031_recovery_conflict.pl b/src/test/recovery/t/031_recovery_conflict.pl
index 7a740f69806..3f52b5c82b6 100644
--- a/src/test/recovery/t/031_recovery_conflict.pl
+++ b/src/test/recovery/t/031_recovery_conflict.pl
@@ -239,7 +239,7 @@ BEGIN;
 LOCK TABLE $table2;
 PREPARE TRANSACTION 'lock';
 INSERT INTO $table1(a) VALUES (170);
-SELECT txid_current();
+SELECT pg_current_xact_id();
 ]);
 
 $node_primary->wait_for_replay_catchup($node_standby);
diff --git a/src/test/recovery/t/040_standby_failover_slots_sync.pl b/src/test/recovery/t/040_standby_failover_slots_sync.pl
index 47d64d05ad1..eb2dde8050d 100644
--- a/src/test/recovery/t/040_standby_failover_slots_sync.pl
+++ b/src/test/recovery/t/040_standby_failover_slots_sync.pl
@@ -433,11 +433,11 @@ $standby1->safe_psql('postgres', "SELECT pg_sync_replication_slots();");
 $primary->safe_psql(
 	'postgres', qq(
 		BEGIN;
-		SELECT txid_current();
+		SELECT pg_current_xact_id();
 		SELECT pg_log_standby_snapshot();
 		COMMIT;
 		BEGIN;
-		SELECT txid_current();
+		SELECT pg_current_xact_id();
 		SELECT pg_log_standby_snapshot();
 		COMMIT;
 ));
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index c743fc769cb..d5ae62fd481 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -1438,7 +1438,7 @@ COMMIT;
 CREATE FUNCTION predicate_stable() RETURNS bool IMMUTABLE
 LANGUAGE plpgsql AS $$
 BEGIN
-  EXECUTE 'SELECT txid_current()';
+  EXECUTE 'SELECT pg_current_xact_id()';
   RETURN true;
 END; $$;
 CREATE INDEX CONCURRENTLY concur_index8 ON concur_heap (f1)
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index eabc9623b20..5f64523c17b 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -513,7 +513,7 @@ COMMIT;
 CREATE FUNCTION predicate_stable() RETURNS bool IMMUTABLE
 LANGUAGE plpgsql AS $$
 BEGIN
-  EXECUTE 'SELECT txid_current()';
+  EXECUTE 'SELECT pg_current_xact_id()';
   RETURN true;
 END; $$;
 CREATE INDEX CONCURRENTLY concur_index8 ON concur_heap (f1)
diff --git a/src/test/subscription/t/035_conflicts.pl b/src/test/subscription/t/035_conflicts.pl
index 426ad74cf33..68c511b6767 100644
--- a/src/test/subscription/t/035_conflicts.pl
+++ b/src/test/subscription/t/035_conflicts.pl
@@ -332,7 +332,7 @@ like(
 	'update target row was deleted in tab');
 
 # Remember the next transaction ID to be assigned
-my $next_xid = $node_A->safe_psql('postgres', "SELECT txid_current() + 1;");
+my $next_xid = $node_A->safe_psql('postgres', "SELECT pg_current_xact_id() + 1;");
 
 # Confirm that the xmin value is advanced to the latest nextXid. If no
 # transactions are running, the apply worker selects nextXid as the candidate
@@ -391,7 +391,7 @@ $node_A->safe_psql('postgres',
 	"ALTER SUBSCRIPTION $subname_AB REFRESH PUBLICATION");
 
 # Remember the next transaction ID to be assigned
-$next_xid = $node_A->safe_psql('postgres', "SELECT txid_current() + 1;");
+$next_xid = $node_A->safe_psql('postgres', "SELECT pg_current_xact_id() + 1;");
 
 # Confirm that the xmin value is advanced to the latest nextXid. If no
 # transactions are running, the apply worker selects nextXid as the candidate
@@ -540,7 +540,7 @@ if ($injection_points_supported != 0)
 
 	# Remember the next transaction ID to be assigned
 	$next_xid =
-	  $node_A->safe_psql('postgres', "SELECT txid_current() + 1;");
+	  $node_A->safe_psql('postgres', "SELECT pg_current_xact_id() + 1;");
 
 	# Confirm that the xmin value is advanced to the latest nextXid after the
 	# prepared transaction on the publisher has been committed.
@@ -591,7 +591,7 @@ $node_B->safe_psql('postgres', "INSERT INTO tab VALUES (5, 5);");
 
 # Advance the xid on Node A to trigger the next cycle of oldest_nonremovable_xid
 # advancement.
-$node_A->safe_psql('postgres', "SELECT txid_current() + 1;");
+$node_A->safe_psql('postgres', "SELECT pg_current_xact_id() + 1;");
 
 $log_offset = -s $node_A->logfile;
 
-- 
2.47.3



^ permalink  raw  reply  [nested|flat] 4+ messages in thread

* Re: Use pg_current_xact_id() instead of deprecated txid_current()
  2026-02-09 12:07 Re: Use pg_current_xact_id() instead of deprecated txid_current() Shinya Kato <[email protected]>
  2026-02-10 06:38 ` Re: Use pg_current_xact_id() instead of deprecated txid_current() Shinya Kato <[email protected]>
@ 2026-05-25 08:09   ` Shinya Kato <[email protected]>
  2026-05-27 17:08     ` Re: Use pg_current_xact_id() instead of deprecated txid_current() lin teletele <[email protected]>
  0 siblings, 1 reply; 4+ messages in thread

From: Shinya Kato @ 2026-05-25 08:09 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Álvaro Herrera <[email protected]>; pgsql-hackers <[email protected]>

On Thu, May 14, 2026 at 9:31 PM Shinya Kato <[email protected]> wrote:
>
> Rebased the patches.

Rebased the patches, again.

-- 
Best regards,
Shinya Kato
NTT OSS Center


Attachments:

  [application/octet-stream] v4-0001-Add-arithmetic-operators-for-xid8.patch (8.1K, 2-v4-0001-Add-arithmetic-operators-for-xid8.patch)
  download | inline diff:
From 0cf63fee403d77aba208b21087a22b7d9c958078 Mon Sep 17 00:00:00 2001
From: Shinya Kato <[email protected]>
Date: Mon, 9 Feb 2026 13:35:07 +0900
Subject: [PATCH v4 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 <[email protected]>
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 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..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 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..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



  [application/octet-stream] v4-0002-Use-pg_current_xact_id-instead-of-deprecated-txid.patch (13.3K, 3-v4-0002-Use-pg_current_xact_id-instead-of-deprecated-txid.patch)
  download | inline diff:
From 3bda1ba8de1093b21fb6600ad6ee69ba873bc71b Mon Sep 17 00:00:00 2001
From: Shinya Kato <[email protected]>
Date: Tue, 10 Feb 2026 13:54:01 +0900
Subject: [PATCH v4 2/2] Use pg_current_xact_id() instead of deprecated
 txid_current()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Replace txid_current() with pg_current_xact_id() in test code.
The previous commit added arithmetic operators for xid8, so
places that need "xid + 1" can now use pg_current_xact_id() + 1
directly.

Author: Shinya Kato <[email protected]>
Reviewed-by: Álvaro Herrera <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/CAOzEurQetW=-1+OnMo8baeVQF=-kAr-wNtFcgRNo+ErPk=xsDQ@mail.gmail.com
---
 contrib/pg_visibility/t/001_concurrent_transaction.pl    | 2 +-
 contrib/test_decoding/expected/slot_creation_error.out   | 6 +++---
 contrib/test_decoding/specs/slot_creation_error.spec     | 2 +-
 src/bin/pg_combinebackup/t/002_compare_backups.pl        | 4 ++--
 src/test/modules/commit_ts/expected/commit_timestamp.out | 4 ++--
 .../modules/commit_ts/expected/commit_timestamp_1.out    | 4 ++--
 src/test/modules/commit_ts/sql/commit_timestamp.sql      | 4 ++--
 src/test/modules/xid_wraparound/t/004_notify_freeze.pl   | 4 ++--
 src/test/recovery/t/021_row_visibility.pl                | 2 +-
 src/test/recovery/t/031_recovery_conflict.pl             | 2 +-
 src/test/recovery/t/040_standby_failover_slots_sync.pl   | 4 ++--
 src/test/regress/expected/create_index.out               | 2 +-
 src/test/regress/sql/create_index.sql                    | 2 +-
 src/test/subscription/t/035_conflicts.pl                 | 9 +++++----
 14 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/contrib/pg_visibility/t/001_concurrent_transaction.pl b/contrib/pg_visibility/t/001_concurrent_transaction.pl
index 3aa556892a6..889cae927fd 100644
--- a/contrib/pg_visibility/t/001_concurrent_transaction.pl
+++ b/contrib/pg_visibility/t/001_concurrent_transaction.pl
@@ -30,7 +30,7 @@ my $bsession = $node->background_psql('other_database');
 $bsession->query_safe(
 	qq[
 	BEGIN;
-	SELECT txid_current();
+	SELECT pg_current_xact_id();
 ]);
 
 # Create a sample table and run vacuum
diff --git a/contrib/test_decoding/expected/slot_creation_error.out b/contrib/test_decoding/expected/slot_creation_error.out
index 25883b508fb..95591752371 100644
--- a/contrib/test_decoding/expected/slot_creation_error.out
+++ b/contrib/test_decoding/expected/slot_creation_error.out
@@ -2,7 +2,7 @@ Parsed test spec with 2 sessions
 
 starting permutation: s1_b s1_xid s2_init s1_view_slot s1_cancel_s2 s1_view_slot s1_c
 step s1_b: BEGIN;
-step s1_xid: SELECT 'xid' FROM txid_current();
+step s1_xid: SELECT 'xid' FROM pg_current_xact_id();
 ?column?
 --------
 xid     
@@ -43,7 +43,7 @@ step s1_c: COMMIT;
 
 starting permutation: s1_b s1_xid s2_init s1_c s1_view_slot s1_drop_slot
 step s1_b: BEGIN;
-step s1_xid: SELECT 'xid' FROM txid_current();
+step s1_xid: SELECT 'xid' FROM pg_current_xact_id();
 ?column?
 --------
 xid     
@@ -78,7 +78,7 @@ pg_drop_replication_slot
 
 starting permutation: s1_b s1_xid s2_init s1_terminate_s2 s1_c s1_view_slot
 step s1_b: BEGIN;
-step s1_xid: SELECT 'xid' FROM txid_current();
+step s1_xid: SELECT 'xid' FROM pg_current_xact_id();
 ?column?
 --------
 xid     
diff --git a/contrib/test_decoding/specs/slot_creation_error.spec b/contrib/test_decoding/specs/slot_creation_error.spec
index d1e35bf58b5..6983f24eae6 100644
--- a/contrib/test_decoding/specs/slot_creation_error.spec
+++ b/contrib/test_decoding/specs/slot_creation_error.spec
@@ -4,7 +4,7 @@ session "s1"
 setup { SET synchronous_commit=on; }
 
 step s1_b { BEGIN; }
-step s1_xid { SELECT 'xid' FROM txid_current(); }
+step s1_xid { SELECT 'xid' FROM pg_current_xact_id(); }
 step s1_c { COMMIT; }
 step s1_cancel_s2 {
     SELECT pg_cancel_backend(pid)
diff --git a/src/bin/pg_combinebackup/t/002_compare_backups.pl b/src/bin/pg_combinebackup/t/002_compare_backups.pl
index b509296a94a..42d252b2242 100644
--- a/src/bin/pg_combinebackup/t/002_compare_backups.pl
+++ b/src/bin/pg_combinebackup/t/002_compare_backups.pl
@@ -105,9 +105,9 @@ my $lsn = $primary->safe_psql('postgres', "SELECT pg_current_wal_lsn();");
 
 # Make sure that the WAL segment containing that LSN has been archived.
 # PostgreSQL won't issue two consecutive XLOG_SWITCH records, and the backup
-# just issued one, so call txid_current() to generate some WAL activity
+# just issued one, so call pg_current_xact_id() to generate some WAL activity
 # before calling pg_switch_wal().
-$primary->safe_psql('postgres', 'SELECT txid_current();');
+$primary->safe_psql('postgres', 'SELECT pg_current_xact_id();');
 $primary->safe_psql('postgres', 'SELECT pg_switch_wal()');
 
 # Now wait for the LSN we chose above to be archived.
diff --git a/src/test/modules/commit_ts/expected/commit_timestamp.out b/src/test/modules/commit_ts/expected/commit_timestamp.out
index bb2fda27681..0d08e0684a2 100644
--- a/src/test/modules/commit_ts/expected/commit_timestamp.out
+++ b/src/test/modules/commit_ts/expected/commit_timestamp.out
@@ -71,7 +71,7 @@ SELECT * FROM pg_xact_commit_timestamp_origin('2'::xid); -- ok, NULL
 (1 row)
 
 -- Test transaction without replication origin
-SELECT txid_current() as txid_no_origin \gset
+SELECT pg_current_xact_id() as txid_no_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        roident != 0 AS valid_roident
@@ -104,7 +104,7 @@ SELECT pg_replication_origin_session_setup('regress_commit_ts: get_origin');
  
 (1 row)
 
-SELECT txid_current() as txid_with_origin \gset
+SELECT pg_current_xact_id() as txid_with_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        r.roname
diff --git a/src/test/modules/commit_ts/expected/commit_timestamp_1.out b/src/test/modules/commit_ts/expected/commit_timestamp_1.out
index f37e701f37a..21f09b89cac 100644
--- a/src/test/modules/commit_ts/expected/commit_timestamp_1.out
+++ b/src/test/modules/commit_ts/expected/commit_timestamp_1.out
@@ -63,7 +63,7 @@ SELECT * FROM pg_xact_commit_timestamp_origin('2'::xid); -- ok, NULL
 (1 row)
 
 -- Test transaction without replication origin
-SELECT txid_current() as txid_no_origin \gset
+SELECT pg_current_xact_id() as txid_no_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        roident != 0 AS valid_roident
@@ -90,7 +90,7 @@ SELECT pg_replication_origin_session_setup('regress_commit_ts: get_origin');
  
 (1 row)
 
-SELECT txid_current() as txid_with_origin \gset
+SELECT pg_current_xact_id() as txid_with_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        r.roname
diff --git a/src/test/modules/commit_ts/sql/commit_timestamp.sql b/src/test/modules/commit_ts/sql/commit_timestamp.sql
index 3bb7bb27a74..a2f0e68317d 100644
--- a/src/test/modules/commit_ts/sql/commit_timestamp.sql
+++ b/src/test/modules/commit_ts/sql/commit_timestamp.sql
@@ -34,7 +34,7 @@ SELECT * FROM pg_xact_commit_timestamp_origin('1'::xid); -- ok, NULL
 SELECT * FROM pg_xact_commit_timestamp_origin('2'::xid); -- ok, NULL
 
 -- Test transaction without replication origin
-SELECT txid_current() as txid_no_origin \gset
+SELECT pg_current_xact_id() as txid_no_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        roident != 0 AS valid_roident
@@ -48,7 +48,7 @@ SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
 SELECT pg_replication_origin_create('regress_commit_ts: get_origin') != 0
   AS valid_roident;
 SELECT pg_replication_origin_session_setup('regress_commit_ts: get_origin');
-SELECT txid_current() as txid_with_origin \gset
+SELECT pg_current_xact_id() as txid_with_origin \gset
 SELECT x.timestamp > '-infinity'::timestamptz AS ts_low,
        x.timestamp <= now() AS ts_high,
        r.roname
diff --git a/src/test/modules/xid_wraparound/t/004_notify_freeze.pl b/src/test/modules/xid_wraparound/t/004_notify_freeze.pl
index d0a1f1fe2fc..9a8300186f7 100644
--- a/src/test/modules/xid_wraparound/t/004_notify_freeze.pl
+++ b/src/test/modules/xid_wraparound/t/004_notify_freeze.pl
@@ -35,9 +35,9 @@ for my $i (1 .. 10)
 }
 
 # Consume enough XIDs to trigger truncation, and one more with
-# 'txid_current' to bump up the freeze horizon.
+# 'pg_current_xact_id' to bump up the freeze horizon.
 $node->safe_psql('postgres', 'select consume_xids(10000000);');
-$node->safe_psql('postgres', 'select txid_current()');
+$node->safe_psql('postgres', 'select pg_current_xact_id()');
 
 # Remember current datfrozenxid before vacuum freeze so that we can
 # check that it is advanced. (Taking the min() this way assumes that
diff --git a/src/test/recovery/t/021_row_visibility.pl b/src/test/recovery/t/021_row_visibility.pl
index 0a4d22b3698..9626d431852 100644
--- a/src/test/recovery/t/021_row_visibility.pl
+++ b/src/test/recovery/t/021_row_visibility.pl
@@ -94,7 +94,7 @@ UPDATE test_visibility SET data = 'first update' RETURNING data;
 		qr/^UPDATE 1$/m),
 	'UPDATE');
 
-$node_primary->psql('postgres', "SELECT txid_current();");  # ensure WAL flush
+$node_primary->psql('postgres', "SELECT pg_current_xact_id();");  # ensure WAL flush
 $node_primary->wait_for_catchup($node_standby);
 
 ok( send_query_and_wait(
diff --git a/src/test/recovery/t/031_recovery_conflict.pl b/src/test/recovery/t/031_recovery_conflict.pl
index 7a740f69806..3f52b5c82b6 100644
--- a/src/test/recovery/t/031_recovery_conflict.pl
+++ b/src/test/recovery/t/031_recovery_conflict.pl
@@ -239,7 +239,7 @@ BEGIN;
 LOCK TABLE $table2;
 PREPARE TRANSACTION 'lock';
 INSERT INTO $table1(a) VALUES (170);
-SELECT txid_current();
+SELECT pg_current_xact_id();
 ]);
 
 $node_primary->wait_for_replay_catchup($node_standby);
diff --git a/src/test/recovery/t/040_standby_failover_slots_sync.pl b/src/test/recovery/t/040_standby_failover_slots_sync.pl
index f8922aaa1a2..ad4a94f5046 100644
--- a/src/test/recovery/t/040_standby_failover_slots_sync.pl
+++ b/src/test/recovery/t/040_standby_failover_slots_sync.pl
@@ -433,11 +433,11 @@ $standby1->safe_psql('postgres', "SELECT pg_sync_replication_slots();");
 $primary->safe_psql(
 	'postgres', qq(
 		BEGIN;
-		SELECT txid_current();
+		SELECT pg_current_xact_id();
 		SELECT pg_log_standby_snapshot();
 		COMMIT;
 		BEGIN;
-		SELECT txid_current();
+		SELECT pg_current_xact_id();
 		SELECT pg_log_standby_snapshot();
 		COMMIT;
 ));
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 55538c4c41e..31cd896dc17 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -1452,7 +1452,7 @@ COMMIT;
 CREATE FUNCTION predicate_stable() RETURNS bool IMMUTABLE
 LANGUAGE plpgsql AS $$
 BEGIN
-  EXECUTE 'SELECT txid_current()';
+  EXECUTE 'SELECT pg_current_xact_id()';
   RETURN true;
 END; $$;
 CREATE INDEX CONCURRENTLY concur_index8 ON concur_heap (f1)
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 82e4062a215..96e2aee98b2 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -517,7 +517,7 @@ COMMIT;
 CREATE FUNCTION predicate_stable() RETURNS bool IMMUTABLE
 LANGUAGE plpgsql AS $$
 BEGIN
-  EXECUTE 'SELECT txid_current()';
+  EXECUTE 'SELECT pg_current_xact_id()';
   RETURN true;
 END; $$;
 CREATE INDEX CONCURRENTLY concur_index8 ON concur_heap (f1)
diff --git a/src/test/subscription/t/035_conflicts.pl b/src/test/subscription/t/035_conflicts.pl
index f23fe6af2a5..82e38f16bc6 100644
--- a/src/test/subscription/t/035_conflicts.pl
+++ b/src/test/subscription/t/035_conflicts.pl
@@ -330,7 +330,7 @@ like(
 	'update target row was deleted in tab');
 
 # Remember the next transaction ID to be assigned
-my $next_xid = $node_A->safe_psql('postgres', "SELECT txid_current() + 1;");
+my $next_xid = $node_A->safe_psql('postgres', "SELECT pg_current_xact_id() + 1;");
 
 # Confirm that the xmin value is advanced to the latest nextXid. If no
 # transactions are running, the apply worker selects nextXid as the candidate
@@ -388,7 +388,7 @@ $node_A->safe_psql('postgres',
 	"ALTER SUBSCRIPTION $subname_AB REFRESH PUBLICATION");
 
 # Remember the next transaction ID to be assigned
-$next_xid = $node_A->safe_psql('postgres', "SELECT txid_current() + 1;");
+$next_xid = $node_A->safe_psql('postgres', "SELECT pg_current_xact_id() + 1;");
 
 # Confirm that the xmin value is advanced to the latest nextXid. If no
 # transactions are running, the apply worker selects nextXid as the candidate
@@ -535,7 +535,8 @@ if ($injection_points_supported != 0)
 		'update target row was deleted in tab');
 
 	# Remember the next transaction ID to be assigned
-	$next_xid = $node_A->safe_psql('postgres', "SELECT txid_current() + 1;");
+	$next_xid =
+	  $node_A->safe_psql('postgres', "SELECT pg_current_xact_id() + 1;");
 
 	# Confirm that the xmin value is advanced to the latest nextXid after the
 	# prepared transaction on the publisher has been committed.
@@ -587,7 +588,7 @@ $node_B->safe_psql('postgres', "INSERT INTO tab VALUES (5, 5);");
 
 # Advance the xid on Node A to trigger the next cycle of oldest_nonremovable_xid
 # advancement.
-$node_A->safe_psql('postgres', "SELECT txid_current() + 1;");
+$node_A->safe_psql('postgres', "SELECT pg_current_xact_id() + 1;");
 
 $log_offset = -s $node_A->logfile;
 
-- 
2.47.3



^ permalink  raw  reply  [nested|flat] 4+ messages in thread

* Re: Use pg_current_xact_id() instead of deprecated txid_current()
  2026-02-09 12:07 Re: Use pg_current_xact_id() instead of deprecated txid_current() Shinya Kato <[email protected]>
  2026-02-10 06:38 ` Re: Use pg_current_xact_id() instead of deprecated txid_current() Shinya Kato <[email protected]>
  2026-05-25 08:09   ` Re: Use pg_current_xact_id() instead of deprecated txid_current() Shinya Kato <[email protected]>
@ 2026-05-27 17:08     ` lin teletele <[email protected]>
  0 siblings, 0 replies; 4+ messages in thread

From: lin teletele @ 2026-05-27 17:08 UTC (permalink / raw)
  To: Shinya Kato <[email protected]>; +Cc: Tom Lane <[email protected]>; Álvaro Herrera <[email protected]>; pgsql-hackers <[email protected]>

Hi Shinya,

Thanks for the patches. I read v4-0001 and have a few small observations
from going through the arithmetic functions. I tested the suggestions
below locally on top of v4 — they pass the existing xid regression tests
and produce identical output on the boundary cases listed in section 3.

1. xid8pl / xid8mi could reuse the helpers in common/int.h
----------------------------------------------------
xid8pl currently rolls its own overflow detection on a mixed-sign
addition:

result = val + (uint64) delta;
if ((delta > 0 && result < val) || (delta < 0 && result > val))
    ereport(ERROR, ...);

This is correct, but it's the only place in the tree that takes this
approach, and the (uint64)-of-a-signed-value plus sign-aware compare
takes a moment to convince oneself of. common/int.h already provides
pg_add_u64_overflow / pg_sub_u64_overflow, plus pg_abs_s64 which returns
uint64 and explicitly handles INT64_MIN, so xid8pl could be written as:

uint64 abs_delta = pg_abs_s64(delta);
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, ...);

And xid8mi symmetrically (add/sub swapped):

uint64 abs_delta = pg_abs_s64(delta);
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, ...);

This keeps the code inside the standard PG overflow-check idiom, and as
a side effect handles a delta of INT64_MIN cleanly (pg_abs_s64 returns
2^63 in that case without invoking UB).

2. INT64_MIN boundary in xid8_mi_xid8
----------------------------------------------------
In the val1 < val2 branch:

if (val2 - val1 > (uint64) PG_INT64_MAX + 1)
    ereport(ERROR, ...);
PG_RETURN_INT64(-((int64) (val2 - val1)));

The bound permits val2 - val1 == 2^63 (e.g. '0'::xid8 -
'9223372036854775808'::xid8). When val2 - val1 == 2^63, the cast
(int64)(val2 - val1) is implementation-defined (the value doesn't fit in
int64), and -INT64_MIN is signed overflow (UB). In practice on
two's-complement targets the answer comes out as INT64_MIN, which is the
correct value, but it relies on UB.

Pulling out the boundary explicitly keeps the same observable behavior
without the UB:

uint64 diff = val2 - val1;

if (diff > (uint64) PG_INT64_MAX + 1)
    ereport(ERROR, ...);
/* diff == 2^63 maps to INT64_MIN */
if (diff > (uint64) PG_INT64_MAX)
    PG_RETURN_INT64(PG_INT64_MIN);
PG_RETURN_INT64(-(int64) diff);

3. Test coverage
----------------------------------------------------
The regression tests in xid.sql exercise the positive overflow side
nicely but miss a few boundaries on the negative side:

-- xid8 - xid8 at the INT64_MIN boundary (#2 above)
select '0'::xid8 - '9223372036854775808'::xid8;

-- xid8 + int8 / xid8 - int8 with INT64_MAX / INT64_MIN deltas
select '0'::xid8 + 9223372036854775807::bigint;
select '0'::xid8 - (-9223372036854775807 - 1)::bigint;
select '9223372036854775807'::xid8 - (-9223372036854775807 - 1)::bigint;

It would be good to pin those down in the expected output.

4. Documentation
----------------------------------------------------
v4-0001 adds four user-visible operators but doesn't touch doc/src/sgml/.
pg_lsn's arithmetic operators are documented in datatype.sgml around the
"pg_lsn Type" section -- it would be nice for the new xid8 operators to
get analogous coverage in the nearby xid8 paragraph.

As a separate observation (probably better as a follow-up thread rather
than expanding the scope of this one): xid8 currently has only hash and
btree opclasses, no BRIN. Since xid8 is strictly monotonic and never
wraps, BRIN minmax looks like a natural fit -- I'll raise that
separately if there's interest.

Regards,
Teletele

On Mon, May 25, 2026 at 4:10 PM Shinya Kato <[email protected]> wrote:
>
> On Thu, May 14, 2026 at 9:31 PM Shinya Kato <[email protected]> wrote:
> >
> > Rebased the patches.
>
> Rebased the patches, again.
>
> --
> Best regards,
> Shinya Kato
> NTT OSS Center






^ permalink  raw  reply  [nested|flat] 4+ messages in thread


end of thread, other threads:[~2026-05-27 17:08 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-02-09 12:07 Re: Use pg_current_xact_id() instead of deprecated txid_current() Shinya Kato <[email protected]>
2026-02-10 06:38 ` Shinya Kato <[email protected]>
2026-05-25 08:09   ` Shinya Kato <[email protected]>
2026-05-27 17:08     ` lin teletele <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox