From dcc2e14b3fc4a738a350dd193878b8365fb26ea7 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <hayato@example.com>
Date: Mon, 23 Feb 2026 12:14:40 +0900
Subject: [PATCH v9 2/2] pg_upgrade: transfer commit timestamps to the new
 cluster

This commit preserves commit timestamps during an upgrade. The advantage of this
commit is not only to provide committed transaction information after the upgrade,
but also to allow detecting {UPDATE|DELETE}_ORIGIN_DIFFERS conflicts for tuples
that were modified before the upgrade.

Files in the pg_commit_ts directory are copied when track_commit_timestamp=on.
Also, pg_resetwal specifies the oldest and newest transaction IDs to the new
cluster.

If the old cluster enables tracking commit timestamps but the new cluster does
not, the pg_upgrade fails to avoid missing them.

Author: Sergey Levin <ls7777@yandex.ru>
Author: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Maxim Orlov <orlovmg.gmail.com>
---
 doc/src/sgml/logical-replication.sgml |  8 ++++----
 src/bin/pg_upgrade/check.c            |  6 +++---
 src/bin/pg_upgrade/pg_upgrade.c       | 12 +++++-------
 3 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml
index 5028fe9af09..3c0db3ab233 100644
--- a/doc/src/sgml/logical-replication.sgml
+++ b/doc/src/sgml/logical-replication.sgml
@@ -2814,11 +2814,11 @@ CONTEXT:  processing remote data for replication origin "pg_16395" during "INSER
 
    <note>
     <para>
-     Commit timestamps and origin data are not preserved during the upgrade.
-     As a result, even if
+     Parameters for physical replication slots are not preserved during the
+     upgrade. As a result, even if
      <link linkend="sql-createsubscription-params-with-retain-dead-tuples"><literal>retain_dead_tuples</literal></link>
-     is enabled, the upgraded subscriber may be unable to detect conflicts or
-     log relevant commit timestamps and origins when applying changes from the
+     is enabled, the upgraded subscriber may be unable to detect
+     <literal>update_deleted</literal> conflicts when applying changes from the
      publisher occurred before the upgrade. Additionally, immediately after the
      upgrade, the vacuum may remove the deleted rows that are required for
      conflict detection. This can affect the changes that were not replicated
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f10753b342e..32352752095 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -793,17 +793,17 @@ check_new_cluster_pg_commit_ts(void)
 {
 	PGconn	   *conn;
 	PGresult   *res;
-	bool		track_commit_timestamp_on;
+	bool		commit_ts_is_enabled;
 
 	prep_status("Checking for new cluster configuration for commit timestamp");
 	conn = connectToServer(&new_cluster, "template1");
 	res = executeQueryOrDie(conn, "SELECT setting FROM pg_settings "
 							"WHERE name = 'track_commit_timestamp'");
-	track_commit_timestamp_on = strcmp(PQgetvalue(res, 0, 0), "on") == 0;
+	commit_ts_is_enabled = strcmp(PQgetvalue(res, 0, 0), "on") == 0;
 	PQclear(res);
 	PQfinish(conn);
 
-	if (!track_commit_timestamp_on &&
+	if (!commit_ts_is_enabled &&
 		old_cluster.controldata.chkpnt_newstCommitTsxid > 0)
 		pg_fatal("\"track_commit_timestamp\" must be \"on\" but is set to \"off\"");
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index b761f6c86c4..bfec731ce6b 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -216,13 +216,11 @@ main(int argc, char **argv)
 	 * as it only retains the dead tuples. It is created here for consistency.
 	 * Note that the new conflict detection slot uses the latest transaction
 	 * ID as xmin, so it cannot protect dead tuples that existed before the
-	 * upgrade. Additionally, commit timestamps and origin data are not
-	 * preserved during the upgrade. So, even after creating the slot, the
-	 * upgraded subscriber may be unable to detect conflicts or log relevant
-	 * commit timestamps and origins when applying changes from the publisher
-	 * occurred before the upgrade especially if those changes were not
-	 * replicated. It can only protect tuples that might be deleted after the
-	 * new cluster starts.
+	 * upgrade. It means even after creating the slot, the upgraded subscriber
+	 * may be unable to detect update_deleted conflicts when applying changes
+	 * from the publisher occurred before the upgrade especially if those
+	 * changes were not replicated. It can only protect tuples that might be
+	 * deleted after the new cluster starts.
 	 */
 	if (migrate_logical_slots || old_cluster.sub_retain_dead_tuples)
 	{
-- 
2.43.0

