From ed125b8869186db8951842ea4f97467056684762 Mon Sep 17 00:00:00 2001
From: Nisha Moond <nisha.moond412@gmail.com>
Date: Fri, 8 May 2026 11:57:55 +0530
Subject: [PATCH v1 2/2] Add TAP test to detect conflict when origin-id is
 reused

---
 .../t/039_origin_reuse_conflict.pl            | 68 +++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 src/test/subscription/t/039_origin_reuse_conflict.pl

diff --git a/src/test/subscription/t/039_origin_reuse_conflict.pl b/src/test/subscription/t/039_origin_reuse_conflict.pl
new file mode 100644
index 00000000000..efc58790f46
--- /dev/null
+++ b/src/test/subscription/t/039_origin_reuse_conflict.pl
@@ -0,0 +1,68 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# Test that roident reuse is detected as an update_origin_differs conflict.
+#
+# sub1 and sub2 both use copy_data=false (no tablesync, one origin each).
+# Dropping sub1 and creating sub2 reuses the same roident.  A row written by
+# sub1's apply worker has the same origin number as sub2, so only
+# IsRoidentReused() can distinguish them.
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $node_pub = PostgreSQL::Test::Cluster->new('publisher');
+$node_pub->init(allows_streaming => 'logical');
+$node_pub->start;
+
+my $node_sub = PostgreSQL::Test::Cluster->new('subscriber');
+$node_sub->init;
+$node_sub->append_conf('postgresql.conf', "track_commit_timestamp = on");
+$node_sub->start;
+
+$node_pub->safe_psql('postgres', "CREATE TABLE t (a int PRIMARY KEY, b text)");
+$node_sub->safe_psql('postgres', "CREATE TABLE t (a int PRIMARY KEY, b text)");
+
+my $pubconn = $node_pub->connstr . ' dbname=postgres';
+$node_pub->safe_psql('postgres', "CREATE PUBLICATION pub FOR TABLE t");
+
+$node_sub->safe_psql('postgres',
+	"CREATE SUBSCRIPTION sub1
+	 CONNECTION '$pubconn'
+	 PUBLICATION pub
+	 WITH (copy_data = false)");
+
+# INSERT flows through sub1's apply worker, stamping the row with roident 1.
+$node_pub->safe_psql('postgres', "INSERT INTO t VALUES (1, 'a')");
+$node_pub->wait_for_catchup('sub1');
+
+# Drop sub1 (frees roident 1), sleep so sub2's ro_created > row's commit_ts.
+$node_sub->safe_psql('postgres', "DROP SUBSCRIPTION sub1");
+$node_sub->safe_psql('postgres', "SELECT pg_sleep(0.05)");
+
+$node_sub->safe_psql('postgres',
+	"CREATE SUBSCRIPTION sub2
+	 CONNECTION '$pubconn'
+	 PUBLICATION pub
+	 WITH (copy_data = false)");
+
+my $log_start = -s $node_sub->logfile;
+
+# The row has origin=roident1 from sub1 with commit_ts=T1.  sub2 also has
+# roident1 but ro_created=T2 > T1, so IsRoidentReused() fires.
+$node_pub->safe_psql('postgres', "UPDATE t SET b = 'b' WHERE a = 1");
+$node_pub->wait_for_catchup('sub2');
+
+my $logfile = slurp_file($node_sub->logfile, $log_start);
+like(
+	$logfile,
+	qr/conflict detected on relation "public\.t": conflict=update_origin_differs/,
+	'update_origin_differs conflict reported for reused roident');
+
+my $val = $node_sub->safe_psql('postgres', "SELECT b FROM t WHERE a = 1");
+is($val, 'b', 'row updated to latest value after conflict');
+
+$node_sub->safe_psql('postgres', "DROP SUBSCRIPTION sub2");
+
+done_testing();
-- 
2.50.1 (Apple Git-155)

